diff --git a/cypress/integration/sampleBlog.spec.js b/cypress/integration/sampleBlog.spec.js index 56b7437ca..3934b5df8 100644 --- a/cypress/integration/sampleBlog.spec.js +++ b/cypress/integration/sampleBlog.spec.js @@ -52,4 +52,14 @@ context('check first integration test', () => { .its('scully-transfer-state') .should('have', 'posts'); }); + + it('Check that the slow user mock template appears then disappears', () => { + cy.visit('/slow').reload(); + + cy.get('app-slow>h1').contains('Scully Not Generated'); + console.log('HERE'); + cy.wait(4100) + .get('app-slow>h1') + .contains('Scully Generated'); + }); }); diff --git a/docs/scully-cmd-line.md b/docs/scully-cmd-line.md index e734e2b38..c9aa5a793 100644 --- a/docs/scully-cmd-line.md +++ b/docs/scully-cmd-line.md @@ -18,6 +18,7 @@ Scully CLI has the following options available: - [ssl](#ssl) - [ssl-cert](#ssl-cert) - [ssl-key](#ssl-key) + - [tds](#tds) ## serve @@ -123,3 +124,18 @@ npx scully serve/watch --ssl --ssl-key=./url/to/file ``` Add url to ssl key file for SSL server + +## tds + +```bash +npx scully --tds +``` + +This launches the Test Data Server. This is only helpful for demos. Currently the following APIs are +supported on the test data server: + +- `/users` - returns a list of users +- `/users/:id` - returns just one user by id +- `/posts` - returns a list of posts +- `/posts/:id` - returns a post by id +- `/slow/:delay` - returns a 200 after the delay has gone by. (eg: `/slow/2000` takes 2 seconds) diff --git a/package-lock.json b/package-lock.json index 85b81d1cd..c67d830d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2461,24 +2461,14 @@ } }, "@types/jsdom": { - "version": "12.2.4", - "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-12.2.4.tgz", - "integrity": "sha512-q+De3S/Ri6U9uPx89YA1XuC+QIBgndIfvBaaJG0pRT8Oqa75k4Mr7G9CRZjIvlbLGIukO/31DFGFJYlQBmXf/A==", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-16.1.0.tgz", + "integrity": "sha512-GiBD8K4tFb0ah+rFAqkoP4tCc6DpCy96lITCCCAf1yqgvmpWNHh4S49sPIBXn4KIfvbVEcDRK+jgDHAbfVFdOw==", "dev": true, - "optional": true, "requires": { "@types/node": "*", - "@types/tough-cookie": "*", - "parse5": "^4.0.0" - }, - "dependencies": { - "parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", - "dev": true, - "optional": true - } + "@types/parse5": "*", + "@types/tough-cookie": "*" } }, "@types/json5": { @@ -2527,6 +2517,12 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "@types/parse5": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-5.0.2.tgz", + "integrity": "sha512-BOl+6KDs4ItndUWUFchy3aEqGdHhw0BC4Uu+qoDonN/f0rbUnJbm71Ulj8Tt9jLFRaAxPLKvdS1bBLfx1qXR9g==", + "dev": true + }, "@types/puppeteer": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/@types/puppeteer/-/puppeteer-1.20.3.tgz", @@ -2855,6 +2851,18 @@ "tslib": "1.10.0" }, "dependencies": { + "@types/jsdom": { + "version": "12.2.4", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-12.2.4.tgz", + "integrity": "sha512-q+De3S/Ri6U9uPx89YA1XuC+QIBgndIfvBaaJG0pRT8Oqa75k4Mr7G9CRZjIvlbLGIukO/31DFGFJYlQBmXf/A==", + "dev": true, + "optional": true, + "requires": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^4.0.0" + } + }, "@types/node": { "version": "12.12.7", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.7.tgz", @@ -2902,6 +2910,13 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true, + "optional": true + }, "supports-color": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", diff --git a/package.json b/package.json index d4f8ec33e..09099411f 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,8 @@ "scully:run": "npm run scully:dev:all -- serve", "scully:run:test": "npm run test && npm run e2e", "scully:r": "node ./dist/scully/scully serve --tds", - "scully:precommit": "npm run scully:compile:all && rm -rf ./dist/static && node ./dist/scully/scully --tds && npm run test", - "scully:compile:all": "ng build @scullyio/ng-lib && ng build --prod && npm run scully:dev:compile", + "scully:precommit": "npm run scully:compile:all && rm -rf ./dist/static && node ./dist/scully/scully --tds --nw && npm run test", + "scully:compile:all": "ng build @scullyio/ng-lib && ng build --prod && npm run scully:dev:compile && npm run flash:build", "generate": "tsc -p ./scully/tsconfig.scully.json && node ./dist/scully/scully --tds", "scully:dev:watch": "tsc -w -p ./scully/tsconfig.scully.json", "scully:dev:compile": "tsc -p ./scully/tsconfig.scully.json", @@ -25,6 +25,7 @@ "scully:publish:major": "tsc -p ./tsconfig.scully.json && npm version major", "scully:test:ssl:self": "npm run generate -- serve --ssl", "scully:test:ssl:file": "npm run generate -- serve --ssl --ssl-cert=./certs/localhost.cert --ssl-key=./certs/localhost.key", + "flash:build": "rm -rf ./dist/scully-plugin-flash-prevention && cp -r projects/scully-plugin-flash-prevention dist/scully-plugin-flash-prevention", "changelog": "npx conventional-changelog -p scully -i ./CHANGELOG.md -s && git add ./CHANGELOG.md", "commit": "git add . && npx git-cz", "commit:select": "npx git-cz", @@ -73,6 +74,7 @@ "@types/jasmine": "~3.4.0", "@types/jasminewd2": "~2.0.3", "@types/jest": "25.1.1", + "@types/jsdom": "^16.1.0", "@types/json5": "^0.0.30", "@types/marked": "^0.7.0", "@types/node": "^12.11.1", diff --git a/projects/sampleBlog/src/app/app-routing.module.ts b/projects/sampleBlog/src/app/app-routing.module.ts index 36c86f257..b92e1e0fc 100644 --- a/projects/sampleBlog/src/app/app-routing.module.ts +++ b/projects/sampleBlog/src/app/app-routing.module.ts @@ -21,6 +21,7 @@ const routes: Routes = [ }, {path: 'demo', loadChildren: () => import('./demo/demo.module').then(m => m.DemoModule)}, {path: 'exclude', loadChildren: () => import('./exclude/exclude.module').then(m => m.ExcludeModule)}, + {path: 'slow', loadChildren: () => import('./slow/slow.module').then(m => m.SlowModule)}, { path: '**', loadChildren: () => import('./pagenotfound/pagenotfound.module').then(m => m.PagenotfoundModule), diff --git a/projects/sampleBlog/src/app/app.module.ts b/projects/sampleBlog/src/app/app.module.ts index 1907f34cb..7143d5ca4 100644 --- a/projects/sampleBlog/src/app/app.module.ts +++ b/projects/sampleBlog/src/app/app.module.ts @@ -11,7 +11,7 @@ import {AppComponent} from './app.component'; BrowserModule, HttpClientModule, AppRoutingModule, - ScullyLibModule.forRoot({useTranferState: true, alwaysMonitor: false}), + ScullyLibModule.forRoot({useTranferState: true, alwaysMonitor: true}), ], bootstrap: [AppComponent], }) diff --git a/projects/sampleBlog/src/app/slow/slow-routing.module.ts b/projects/sampleBlog/src/app/slow/slow-routing.module.ts new file mode 100644 index 000000000..be1f6bdb8 --- /dev/null +++ b/projects/sampleBlog/src/app/slow/slow-routing.module.ts @@ -0,0 +1,12 @@ +import {NgModule} from '@angular/core'; +import {Routes, RouterModule} from '@angular/router'; + +import {SlowComponent} from './slow.component'; + +const routes: Routes = [{path: '', component: SlowComponent}]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class SlowRoutingModule {} diff --git a/projects/sampleBlog/src/app/slow/slow.component.spec.ts b/projects/sampleBlog/src/app/slow/slow.component.spec.ts new file mode 100644 index 000000000..accb505cc --- /dev/null +++ b/projects/sampleBlog/src/app/slow/slow.component.spec.ts @@ -0,0 +1,24 @@ +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; + +import {SlowComponent} from './slow.component'; + +describe('SlowComponent', () => { + let component: SlowComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [SlowComponent], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SlowComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/sampleBlog/src/app/slow/slow.component.ts b/projects/sampleBlog/src/app/slow/slow.component.ts new file mode 100644 index 000000000..3689905b8 --- /dev/null +++ b/projects/sampleBlog/src/app/slow/slow.component.ts @@ -0,0 +1,23 @@ +import {HttpClient} from '@angular/common/http'; +import {Component} from '@angular/core'; +import {isScullyGenerated} from '@scullyio/ng-lib'; +import {first} from 'rxjs/operators'; + +@Component({ + selector: 'app-slow', + template: ` +

slow works!

+

Scully Not Generated

+

Scully Generated

+ `, + styles: [``], +}) +export class SlowComponent { + isGenerated = isScullyGenerated(); + + delay$ = this.http.get('http://localhost:8200/slow/4000'); + + constructor(private http: HttpClient) { + this.delay$.pipe(first()).subscribe(); + } +} diff --git a/projects/sampleBlog/src/app/slow/slow.module.ts b/projects/sampleBlog/src/app/slow/slow.module.ts new file mode 100644 index 000000000..31a7fdb27 --- /dev/null +++ b/projects/sampleBlog/src/app/slow/slow.module.ts @@ -0,0 +1,11 @@ +import {NgModule} from '@angular/core'; +import {CommonModule} from '@angular/common'; + +import {SlowRoutingModule} from './slow-routing.module'; +import {SlowComponent} from './slow.component'; + +@NgModule({ + declarations: [SlowComponent], + imports: [CommonModule, SlowRoutingModule], +}) +export class SlowModule {} diff --git a/projects/sampleBlog/src/index.html b/projects/sampleBlog/src/index.html index f91e26fe2..265809868 100644 --- a/projects/sampleBlog/src/index.html +++ b/projects/sampleBlog/src/index.html @@ -9,6 +9,6 @@ - + diff --git a/projects/scully-plugin-flash-prevention/README.md b/projects/scully-plugin-flash-prevention/README.md new file mode 100644 index 000000000..e98ee07f1 --- /dev/null +++ b/projects/scully-plugin-flash-prevention/README.md @@ -0,0 +1,102 @@ +# ScullyPluginFlashPrevention + +The `scully-plugin-flash-prevention` is a postRenderer that helps you hide any flashes that your +app may be experiencing once you add Scully to your project. + +After adding Scully, your app will appear instantly because the pre-rendered HTML and CSS is +immediately available. After appearing instantly, the JavaScript and CSS files will download and +then your Angular app will bootstrap (init). When it bootstraps, the pre-rendered version may +disappear for a moment, and then once the app is ready, the view will re-appear. This +disappearing-then-appearing is very normal for apps that are pre-rendered on a server. This +project is to prevent that. + +This project shows the pre-rendered copy of your app until your app is fully render and the +flash is over. It then shows your app and deletes the copy. + +### How it works + +Before this plugin, you app would pre-render and then save to file, like this: + +```html + + // The entire content of your app here + +``` + +After this plugin, you will see the following in your pre-rendered template: + +```html + + + // The entire content of your app here + +``` + +This `app-root-scully` will be the pre-rendered copy of your app. Prior to your app being +rendered fully, `app-root` will be hidden and `app-root-scully` will be displayed. Once your +app has fully bootstrapped, `app-root-scully` will be hidden and then 100ms later removed +from the DOM. The mechanism that shows and hides these two is CSS. There is some CSS added +during the Scully build that looks like the following: + +```css +body:not(.loaded) app-root { + display: none; +} +body.loaded app-root-scully { + display: inherit; +} +``` + +Once the app has been fully loaded, the `loaded` class is added to the `` tag. + +And that's how it all works!!! + +## Getting Started + +**1 -** Install the package: `npm install -d scully-plugin-flash-prevention` + +**2 -** Add the postRenderer to your `scully.config`: + +```javascript +// Add this line to your imports +const { getFlashPreventionPlugin } = require('scully-plugin-flash-prevention'); + +// Add the following to your `scully.config.postRenderers` +exports.config = { + ... + postRenderers : [getFlashPreventionPlugin({appRootSelector: 'custom-app-root'})], + ... +} +``` + +You only need to pass the `{appRootSelector: 'custom-app-root'}` if your app has a selector other +than `app-root`. It is defaulted to `app-root`. + +**3 -** Update `app.module` to include `alwaysMonitor` in the `ScullyLibModule.forRoot` call. + +```typescript +ScullyLibModule.forRoot({ + useTransferState: true, + alwaysMonitor: true, <-- Add this line to your `app.module.ts` +}); +``` + +**4 -** Apply any styles from `app-root` to `app-root-scully` as well. Any styles that are in your +`app.component.(css|scss|less)` need to be applied to the copy of your app that was made. This means +that you need to possibly move any styles that apply to the `app-root` specifically, and put them +in a location where you can also make those styles apply to `app-root-scully` as well. See here: + +```css +// BEFORE +app-root { + ... some styles; +} + +// AFTER +app-root, +app-root-scully { + ... some styles; +} +``` + +That's all it takes to get set up. diff --git a/projects/scully-plugin-flash-prevention/package.json b/projects/scully-plugin-flash-prevention/package.json new file mode 100644 index 000000000..e4986d529 --- /dev/null +++ b/projects/scully-plugin-flash-prevention/package.json @@ -0,0 +1,14 @@ +{ + "name": "scully-plugin-flash-prevention", + "version": "0.0.32", + "peerDependencies": { + "@angular/common": "^9.0.0-rc.9", + "@angular/core": "^9.0.0-rc.9", + "@scullyio/scully": "^0.0.78", + "tslib": "^1.10.0" + }, + "main": "src/lib/public-apis.js", + "contributors": [ + "Aaron Frost " + ] +} diff --git a/projects/scully-plugin-flash-prevention/src/lib/flash-prevention.plugin.js b/projects/scully-plugin-flash-prevention/src/lib/flash-prevention.plugin.js new file mode 100644 index 000000000..6b4efd30d --- /dev/null +++ b/projects/scully-plugin-flash-prevention/src/lib/flash-prevention.plugin.js @@ -0,0 +1,89 @@ +const {registerPlugin} = require('../../../../dist/scully'); +const {appendToHead} = require('./utils'); + +let AppRootSelector = 'app-root'; +let LoadedClass = 'loaded'; +const DiscountFlashPrevention = 'discountFlashPrevention'; +const validator = async () => {}; +registerPlugin('render', DiscountFlashPrevention, discountFlashPreventionPlugin, validator); +registerPlugin('router', DiscountFlashPrevention, async ur => [{route: ur}], validator); + +module.exports = { + getDiscountFlashPreventionPlugin, +}; + +function getDiscountFlashPreventionPlugin({appRootSelector, appLoadedClass} = {}) { + if (appRootSelector) { + AppRootSelector = appRootSelector; + } + if (appLoadedClass) { + LoadedClass = appLoadedClass; + } + + return DiscountFlashPrevention; +} + +async function discountFlashPreventionPlugin(html, handledRoute) { + let newHtml = await createSecondAppRoot(html); + newHtml = await addBitsToHead(newHtml); + return newHtml; +} + +async function createSecondAppRoot(html) { + const appRootSelector = AppRootSelector; + const appRootStartRegExp = new RegExp(`\<${appRootSelector}[^>]*\>`, 'g'); + const appRootEndRegExp = new RegExp(`\<\/${appRootSelector}\>`, 'g'); + let [openTagMatch] = html.match(appRootStartRegExp); + const [closeTagMatch] = html.match(appRootEndRegExp); + const blackListAttrs = ['_nghost', 'ng-version']; + let cleanedOpenTag = openTagMatch + .replace('>', ' >') + .split(' ') + .reduce((acc, attr) => { + // Does the html attr exist in the blacklisted attrs + const attrIsBlacklisted = blackListAttrs.reduce((acc, cur) => { + if (acc === true) return acc; + + return attr.startsWith(cur); + }, false); + + if (!attrIsBlacklisted) { + acc.push(attr); + } + return acc; + }, []) + .join(' ') + .replace(' >', '>'); + let newHtml = html + // replace the closing tag with replacement scully closing tag + .replace(closeTagMatch, `${closeTagMatch.replace(appRootSelector, `${appRootSelector}-scully`)}`) + // replace opening tag with cleaned app root tag AND replacement scully app root tag + .replace( + openTagMatch, + `${cleanedOpenTag}${closeTagMatch}${openTagMatch.replace(appRootSelector, `${appRootSelector}-scully`)}` + ); + ``; + return newHtml; +} + +async function addBitsToHead(html) { + const contentScript = ` + + +`; + + return appendToHead(html, contentScript); +} diff --git a/projects/scully-plugin-flash-prevention/src/lib/utils.js b/projects/scully-plugin-flash-prevention/src/lib/utils.js new file mode 100644 index 000000000..703c49ef0 --- /dev/null +++ b/projects/scully-plugin-flash-prevention/src/lib/utils.js @@ -0,0 +1,12 @@ +const headEndTag = ``; + +module.exports = { + appendToHead, +}; + +async function appendToHead(html, str) { + const indexOfFirstHeadClose = html.indexOf(headEndTag); + const start = html.slice(0, indexOfFirstHeadClose); + const end = html.slice(indexOfFirstHeadClose); + return `${start}${str}${end}`; +} diff --git a/projects/scully-plugin-flash-prevention/src/public-api.js b/projects/scully-plugin-flash-prevention/src/public-api.js new file mode 100644 index 000000000..fc48e4a30 --- /dev/null +++ b/projects/scully-plugin-flash-prevention/src/public-api.js @@ -0,0 +1,7 @@ +/* + * Public API Surface of scully-plugin-flash-prevention + */ + +const {getDiscountFlashPreventionPlugin} = require('./lib/flash-prevention.plugin'); + +module.exports = {getDiscountFlashPreventionPlugin}; diff --git a/scully.sampleBlog.config.js b/scully.sampleBlog.config.js index 461cb3f98..2657c821b 100644 --- a/scully.sampleBlog.config.js +++ b/scully.sampleBlog.config.js @@ -1,9 +1,14 @@ -const {join} = require('path'); +const { + getDiscountFlashPreventionPlugin, +} = require('./projects/scully-plugin-flash-prevention/src/public-api'); + /** load the plugin */ require('./extraPlugin/extra-plugin.js'); require('./extraPlugin/tocPlugin'); require('./extraPlugin/voidPlugin'); +const FlashPrevention = getDiscountFlashPreventionPlugin(); + exports.config = { /** outDir is where the static distribution files end up */ outDir: './dist/static', @@ -71,12 +76,9 @@ exports.config = { folder: './blog', }, }, - '/blog/:slug': { - type: 'contentFolder', - // postRenderers: ['toc'], - slug: { - folder: './blog', - }, + '/slow': { + type: FlashPrevention, + postRenderers: [FlashPrevention], }, }, guessParserOptions: { diff --git a/scully/utils/dataServer.ts b/scully/utils/dataServer.ts index 98880f265..f907b3dfd 100644 --- a/scully/utils/dataServer.ts +++ b/scully/utils/dataServer.ts @@ -29,6 +29,19 @@ export async function startDataServer(ssl: boolean) { res.json(posts); }); dataServer.get('/posts/:id', (req, res) => res.json(posts.find(row => row.id === +req.params.id))); + dataServer.get('/slow/:delay', (req, res) => { + const {delay: d} = req.params; + let delay; + try { + delay = parseInt(d, 10); + } catch (e) { + delay = 1000; + } + if (delay === NaN) { + delay = 1000; + } + setTimeout(() => res.json({delay, status: 'OK'}), delay); + }); return dataServer.listen(8200, scullyConfig.hostName, x => { log(`Test data server started on "${yellow(`http://${scullyConfig.hostName}:${8200}/`)}" `); }); diff --git a/src/__tests__/__snapshots__/blog-index.spec.ts.snap b/src/__tests__/__snapshots__/blog-index.spec.ts.snap index 07c717d74..ed459fe34 100644 --- a/src/__tests__/__snapshots__/blog-index.spec.ts.snap +++ b/src/__tests__/__snapshots__/blog-index.spec.ts.snap @@ -17,7 +17,7 @@ exports[`ContentFolder: Test blog/page-1 Check contentPlugin render 1`] = ` - Scully demo blog app! rendering inside scully🏠

Scully blog content

+ Scully demo blog app! rendering inside scully🏠

Scully blog content


@@ -50,7 +50,7 @@ exports[`Static: Test blog index Check clean blog index by scully 1`] = ` - Scully demo blog app! rendering inside scully🏠

Overview of blog posts

Deeply embedded

11/27/19

This is the deep folder demo page in this sample.

My first page

11/26/19

This is the first demo page in this sample.

My third page

11/28/19

At this point, I should write something different in here.

The Rainbow Cat, A free book out of the Guttenberg library

12/16/19

/blog/page-5

My test sub page

11/27/19

This is the second demo page in this sample.

Made with ❤️ @HeroDevs

+ Scully demo blog app! rendering inside scully🏠

Overview of blog posts

Deeply embedded

11/27/19

This is the deep folder demo page in this sample.

My first page

11/26/19

This is the first demo page in this sample.

My third page

11/28/19

At this point, I should write something different in here.

The Rainbow Cat, A free book out of the Guttenberg library

12/16/19

/blog/page-5

My test sub page

11/27/19

This is the second demo page in this sample.

Made with ❤️ @HeroDevs

" diff --git a/src/__tests__/__snapshots__/home.spec.ts.snap b/src/__tests__/__snapshots__/home.spec.ts.snap index b4799e8ec..5cb8faee1 100644 --- a/src/__tests__/__snapshots__/home.spec.ts.snap +++ b/src/__tests__/__snapshots__/home.spec.ts.snap @@ -10,7 +10,7 @@ exports[`Check list of all Check clean all list from scully 1`] = ` - Scully demo blog app! rendering inside scully🏠

Available routes

Top level routes onlyUnpublished routes

Made with ❤️ @HeroDevs

+ Scully demo blog app! rendering inside scully🏠

Available routes

Top level routes onlyUnpublished routes

Made with ❤️ @HeroDevs

" diff --git a/src/__tests__/__snapshots__/users.spec.ts.snap b/src/__tests__/__snapshots__/users.spec.ts.snap index 94691ab27..264fe5237 100644 --- a/src/__tests__/__snapshots__/users.spec.ts.snap +++ b/src/__tests__/__snapshots__/users.spec.ts.snap @@ -10,7 +10,7 @@ exports[`JsonPlugin: test user List Check clean blog index by scully 1`] = ` - Scully demo blog app! rendering inside scully🏠

Users

IdUsername
1 Leanne Graham
2 Ervin Howell
3 Clementine Bauch
4 Patricia Lebsack
5 Chelsey Dietrich
6 Mrs. Dennis Schulist
7 Kurtis Weissnat
8 Nicholas Runolfsdottir V
9 Glenna Reichert
10 Clementina DuBuque

Made with ❤️ @HeroDevs

+ Scully demo blog app! rendering inside scully🏠

Users

IdUsername
1 Leanne Graham
2 Ervin Howell
3 Clementine Bauch
4 Patricia Lebsack
5 Chelsey Dietrich
6 Mrs. Dennis Schulist
7 Kurtis Weissnat
8 Nicholas Runolfsdottir V
9 Glenna Reichert
10 Clementina DuBuque

Made with ❤️ @HeroDevs

" diff --git a/src/__tests__/flash-prevention.spec.ts b/src/__tests__/flash-prevention.spec.ts new file mode 100644 index 000000000..b5dd985a7 --- /dev/null +++ b/src/__tests__/flash-prevention.spec.ts @@ -0,0 +1,54 @@ +import {readFileSync} from 'fs'; +import {join} from 'path'; +import {replaceIndexNG} from '../test-config.helper'; +import {JSDOM, DOMWindow} from 'jsdom'; + +describe('Flash Prevention: Test Flash Prevention', () => { + let index: string; + let dom, window: DOMWindow; + + beforeEach(() => { + index = readFileSync(join(__dirname, '../../dist/static/slow/index.html'), 'UTF8').toString(); + dom = new JSDOM(index); + window = dom.window; + }); + + it(`Make sure both elements appear`, () => { + const cleanIndex = replaceIndexNG(index); + const indexOfAppRootStart = cleanIndex.indexOf(''); + const indexOfMockAppRootStart = cleanIndex.indexOf(''); + expect(indexOfAppRootStart).not.toBe(-1); + expect(indexOfAppRootEnd).not.toBe(-1); + expect(indexOfMockAppRootStart).not.toBe(-1); + expect(indexOfMockAppRootEnd).not.toBe(-1); + }); + + it(`Make sure blacklisted attributes are removed`, () => { + let appRoot = window.document.querySelector('app-root'); + let mockRoot = window.document.querySelector('app-root-scully'); + let appRootAttributes = Object.getOwnPropertyNames(appRoot.attributes); + let mockRootAttributes = Object.getOwnPropertyNames(mockRoot.attributes); + + let appRootHasNgVersion = appRoot.hasAttribute('ng-version'); + let mockRootHasNgVersion = mockRoot.hasAttribute('ng-version'); + expect(appRootHasNgVersion).toBe(false); + expect(mockRootHasNgVersion).toBe(true); + + let appRootHasNgHost = appRootAttributes.reduce((a, c) => a || c.startsWith('_nghost'), false); + let mockRootHasNgHost = mockRootAttributes.reduce((a, c) => a || c.startsWith('_nghost'), false); + expect(appRootHasNgHost).toBe(false); + expect(mockRootHasNgHost).toBe(true); + }); + + it(`Make sure non-blacklisted attributes stay`, () => { + let appRoot = window.document.querySelector('app-root'); + let mockRoot = window.document.querySelector('app-root-scully'); + let appRootHasClassFoo = appRoot.classList.contains('foo'); + let mockRootHasClassFoo = appRoot.classList.contains('foo'); + + expect(appRootHasClassFoo).toBe(true); + expect(mockRootHasClassFoo).toBe(true); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 7e1215e04..6c8ec610e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,7 +17,8 @@ "lib": ["es2018", "dom"], "paths": { "@scullyio/scully": ["scully"], - "@scullyio/ng-lib": ["dist/scullyio/ng-lib"] + "@scullyio/ng-lib": ["dist/scullyio/ng-lib"], + "scully-plugin-flash-prevention": ["dist/scully-plugin-flash-prevention"] } }, "exclude": ["dist/scully/**/*"],