diff --git a/apps/sample-blog/src/app/slow/slow.component.ts b/apps/sample-blog/src/app/slow/slow.component.ts index 2099337e3..3bfe0b227 100644 --- a/apps/sample-blog/src/app/slow/slow.component.ts +++ b/apps/sample-blog/src/app/slow/slow.component.ts @@ -15,7 +15,7 @@ import { first } from 'rxjs/operators'; export class SlowComponent { isGenerated = isScullyGenerated(); - delay$ = this.http.get('http://localhost:8200/slow/4000'); + delay$ = this.http.get('http://localhost:8200/slow/2000'); constructor(private http: HttpClient) { this.delay$.pipe(first()).subscribe(); diff --git a/apps/sample-blog/src/app/static/static.component.html b/apps/sample-blog/src/app/static/static.component.html index 4af243315..abaca58f0 100644 --- a/apps/sample-blog/src/app/static/static.component.html +++ b/apps/sample-blog/src/app/static/static.component.html @@ -1,4 +1,5 @@ -

Available routes

+ +

{{ title$ | async }}

Top level routes only console.log('current route', r)), + map(r => r.title || ''), + tap(t => this.title.setTitle(t)) + ); constructor( private srs: ScullyRoutesService, - private route: ActivatedRoute + private route: ActivatedRoute, + private title: Title ) {} get routes() { diff --git a/guess b/guess deleted file mode 160000 index 180dcd23e..000000000 --- a/guess +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 180dcd23ecf0adbe2dcc5e84648ba9963c3e9479 diff --git a/libs/ng-lib/src/lib/idleMonitor/idle-monitor.service.ts b/libs/ng-lib/src/lib/idleMonitor/idle-monitor.service.ts index 208745bcd..c5ab5c692 100644 --- a/libs/ng-lib/src/lib/idleMonitor/idle-monitor.service.ts +++ b/libs/ng-lib/src/lib/idleMonitor/idle-monitor.service.ts @@ -29,11 +29,11 @@ declare global { } } -if (window) { - window.addEventListener('AngularReady', ev => { - console.log('appReady fired', ev); - }); -} +// if (window) { +// window.addEventListener('AngularReady', ev => { +// console.log('appReady fired', ev); +// }); +// } @Injectable({ providedIn: 'root' @@ -149,8 +149,13 @@ export class IdleMonitorService { }, 50); return; } - window.dispatchEvent(this.appReady); - this.setState('idle', true); + this.zone.run(() => { + /** run this inside the zone, and give the app 250Ms to wrap up, before scraping starts */ + setTimeout(() => { + window.dispatchEvent(this.appReady); + this.setState('idle', true); + }, 250); + }); }; monitor(); }); @@ -172,6 +177,6 @@ export class IdleMonitorService { } } -function dropEndingSlash(str: string) { +export function dropEndingSlash(str: string) { return str.endsWith('/') ? str.slice(0, -1) : str; } diff --git a/libs/ng-lib/src/lib/route-service/scully-routes.service.ts b/libs/ng-lib/src/lib/route-service/scully-routes.service.ts index 8062d88b7..bb93e5c44 100644 --- a/libs/ng-lib/src/lib/route-service/scully-routes.service.ts +++ b/libs/ng-lib/src/lib/route-service/scully-routes.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { Observable, of, ReplaySubject } from 'rxjs'; +import { Observable, of, ReplaySubject, merge } from 'rxjs'; import { catchError, map, @@ -8,6 +8,8 @@ import { filter } from 'rxjs/operators'; import { fetchHttp } from '../utils/fetchHttp'; +import { Router, NavigationEnd, NavigationStart } from '@angular/router'; +import { basePathOnly } from '../utils/basePathOnly'; export interface ScullyRoute { route: string; @@ -25,6 +27,9 @@ export interface ScullyRoute { }) export class ScullyRoutesService { private refresh = new ReplaySubject(1); + /** + * An observable with all routes, published and unpublished alike + */ allRoutes$: Observable = this.refresh.pipe( switchMap(() => fetchHttp('/assets/scully-routes.json')), catchError(() => { @@ -38,6 +43,9 @@ export class ScullyRoutesService { map(this.cleanDups), shareReplay({ refCount: false, bufferSize: 1 }) ); + /** + * An observable with available routes (all published routes) + */ available$ = this.allRoutes$.pipe( map(list => list.filter(r => @@ -47,6 +55,9 @@ export class ScullyRoutesService { shareReplay({ refCount: false, bufferSize: 1 }) ); + /** + * an observable with all unpublished routes + */ unPublished$ = this.allRoutes$.pipe( map(list => list.filter(r => @@ -56,6 +67,10 @@ export class ScullyRoutesService { shareReplay({ refCount: false, bufferSize: 1 }) ); + /** + * An observable with the top-level off all published routes. + * (in an urls it would be `http://www.sample.org/__thisPart__/subroutes`) + */ topLevel$: Observable = this.available$.pipe( map(routes => routes.filter((r: ScullyRoute) => !r.route.slice(1).includes('/')) @@ -63,36 +78,50 @@ export class ScullyRoutesService { shareReplay({ refCount: false, bufferSize: 1 }) ); - constructor() { + constructor(private router: Router) { /** kick off first cycle */ this.reload(); } + /** + * returns an observable that returns the route information for the + * route currently selected. subscribes to route-events to update when needed + */ getCurrent(): Observable { if (!location) { /** probably not in a browser, no current location available */ return of(); } - const curLocation = decodeURI(location.pathname).trim(); - return this.available$.pipe( - map(list => - list.find( + /** fire off at start, and when navigation is done. */ + return merge(of(new NavigationEnd(0, '', '')), this.router.events).pipe( + filter(e => e instanceof NavigationEnd), + switchMap(() => this.available$), + map(list => { + const curLocation = basePathOnly(encodeURI(location.pathname).trim()); + return list.find( r => - curLocation === r.route.trim() || + curLocation === basePathOnly(r.route.trim()) || (r.slugs && Array.isArray(r.slugs) && - r.slugs.find(slug => curLocation.endsWith(slug.trim()))) - ) - ) + r.slugs.find(slug => + curLocation.endsWith(basePathOnly(slug.trim())) + )) + ); + }) ); } + /** + * internal, as routes can have multiple slugs, and so occur multiple times + * this util function collapses all slugs back into 1 route. + */ private cleanDups(routes: ScullyRoute[]) { const m = new Map(); routes.forEach(r => m.set(r.sourceFile || r.route, r)); return [...m.values()]; } + /** an utility that will force a reload of the `scully-routes.json` file */ reload(): void { this.refresh.next(); } diff --git a/libs/plugins/from-data/src/lib/plugins-from-data.ts b/libs/plugins/from-data/src/lib/plugins-from-data.ts index 3ff025d89..6f70e5ef3 100644 --- a/libs/plugins/from-data/src/lib/plugins-from-data.ts +++ b/libs/plugins/from-data/src/lib/plugins-from-data.ts @@ -1,12 +1,14 @@ import { routeSplit, registerPlugin, HandledRoute } from '@scullyio/scully'; +export const extraData = 'extraData'; + /** * This plugin replaces the parameter with a counter from 0 to the numberOfPages * in the config. * @param route * @param options */ -export const dataRoutesPlugin = async ( +const dataRoutesPlugin = async ( route, options ): Promise[]> => { diff --git a/libs/scully/src/lib/systemPlugins/storeRoutes.ts b/libs/scully/src/lib/systemPlugins/storeRoutes.ts index f185936f2..e093b2473 100644 --- a/libs/scully/src/lib/systemPlugins/storeRoutes.ts +++ b/libs/scully/src/lib/systemPlugins/storeRoutes.ts @@ -24,7 +24,7 @@ export async function storeRoutes(routes: HandledRoute[]) { } try { const jsonResult = JSON.stringify( - routes.map(r => ({ route: r.route || '/', ...r.data })) + routes.map(r => ({ route: r.route || '/', title: r['title'], ...r.data })) ); const write = file => { createFolderFor(file); diff --git a/tests/cypress/integration/getcurrent.spec.js b/tests/cypress/integration/getcurrent.spec.js new file mode 100644 index 000000000..50629ff4b --- /dev/null +++ b/tests/cypress/integration/getcurrent.spec.js @@ -0,0 +1,19 @@ +/// + +const { textSpanContainsTextSpan } = require('typescript'); + +context('RoutesService tests', () => { + it('should start with toplevel', () => { + cy.visit('/home'); + cy.get('main>app-static>h1').should(el => + assert.isTrue(el.html() === 'Toplevel routes in application') + ); + }); + + it('navigate to all', () => { + cy.get('main > app-static > a:nth-child(2)') + .click() + .get('main>app-static>h1') + .should(el => assert.isTrue(el.html() === 'All routes in application')); + }); +}); diff --git a/tests/cypress/integration/sampleBlog.spec.js b/tests/cypress/integration/sampleBlog.spec.js index 6a1abe952..e53feeb60 100644 --- a/tests/cypress/integration/sampleBlog.spec.js +++ b/tests/cypress/integration/sampleBlog.spec.js @@ -1,6 +1,6 @@ /// -context('check first integration test', () => { +context('combined integration tests', () => { it('check if a users exist', () => { cy.visit('/home'); cy.get('ul>li>a') @@ -28,7 +28,7 @@ context('check first integration test', () => { onRequest: req => { cy.log('Call http done'); expect(true).to.equal(false); - }, + } }); cy.visit('/user'); cy.wait(3000); @@ -37,15 +37,15 @@ context('check first integration test', () => { it('Check the list of users after navigation', () => { cy.visit('/home'); cy.get('ul>li>a') - .contains('/user', {timeout: 1250}) + .contains('/user', { timeout: 1250 }) .click() .wait(500) .get('a') - .contains('Leanne', {timeout: 1250}) + .contains('Leanne', { timeout: 1250 }) .click() .wait(5) .get('p') - .contains('1', {timeout: 1250}); + .contains('1', { timeout: 1250 }); }); it('Check of transferState exist in html', () => { @@ -77,7 +77,9 @@ context('check first integration test', () => { }); it('check link to have target_blank in blog page 2', () => { - cy.visit('/blog/___UNPUBLISHED___k5nhcflm_SJwD4Z0QDrIHg1PGHo2mrfLZE8sfUsPy/'); + cy.visit( + '/blog/___UNPUBLISHED___k5nhcflm_SJwD4Z0QDrIHg1PGHo2mrfLZE8sfUsPy/' + ); cy.get('a[target]').should('have.attr', 'target', '_blank'); }); @@ -86,7 +88,7 @@ context('check first integration test', () => { cy.visit('/slow').reload(); cy.get('app-slow>h1').contains('Scully Not Generated'); - cy.wait(4100) + cy.wait(2100) .get('app-slow>h1') .contains('Scully Generated'); }); diff --git a/tests/jest/src/__tests__/__snapshots__/home.spec.ts.snap b/tests/jest/src/__tests__/__snapshots__/home.spec.ts.snap index c2fe1fd45..206bb26ac 100644 --- a/tests/jest/src/__tests__/__snapshots__/home.spec.ts.snap +++ b/tests/jest/src/__tests__/__snapshots__/home.spec.ts.snap @@ -3,7 +3,7 @@ exports[`Check list of all Check clean all list from scully 1`] = ` " - SampleBlog + All routes in application @@ -11,7 +11,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🏠

All routes in application

Top level routes onlyUnpublished routes

Made with ❤️ @HeroDevs

" diff --git a/tsconfig.json b/tsconfig.json index a701dd95f..91eac843c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -31,5 +31,5 @@ "angularCompilerOptions": { "disableTypeScriptVersionCheck": true }, - "exclude": ["node_modules", "tmp"] + "exclude": ["node_modules", "tmp", "dist"] }