Skip to content

Commit

Permalink
Fix activation flow
Browse files Browse the repository at this point in the history
  • Loading branch information
sebdoucet committed Sep 13, 2024
1 parent 859872c commit ca0d0ad
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 8 deletions.
6 changes: 6 additions & 0 deletions src/application-part/single-page-application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ApplicationPart } from "./application-part";
import { ApplicationPartBuilder } from "./application-part-builder";
import { ChildApplicationPartBuilder } from "./child-application-part";
import { DefaultApplicationConfigureHandler } from "./default-application-configure-handler";
import { INavigationService } from "../navigation";

class SinglePageAppBuilder extends ApplicationPartBuilder {

Expand All @@ -25,6 +26,11 @@ export class SinglePageApplication extends ApplicationPart {
super(builder);
}

navigate(url: string, replace?: boolean): Promise<void> {
return this.services.get(INavigationService, true)
.navigate(url, replace);
}

protected createAppBuilder(name: string): IApplicationPartBuilder {
const result = new Delayed<IApplicationPart>();
this.addChild(result);
Expand Down
15 changes: 7 additions & 8 deletions src/routing/default-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ export class DefaultRouter implements IRouter {
const routeHandler = await this.resolveHandler(cursor);

if (!routeHandler) {
this._logger.warn(null, `No match found for the remaining route path: {relativeUrl}`, cursor.remainingPath, ...this._routingTable.getPaths());
if (!parent) {
this._logger.warn(null, `No match found for the remaining route path: {relativeUrl}`, cursor.remainingPath, ...this._routingTable.getPaths());
}
return { success: false, reason: `No match found for the remaining route path: ${cursor.remainingPath}` };
}

Expand Down Expand Up @@ -140,21 +142,18 @@ export class DefaultRouter implements IRouter {
return RoutingResult.failure("Error while invoking routing handler");
}

private async invokeChildren(root:string, cursor: RouteResolutionCursor, ctx: RoutingInvocationContext): Promise<void> {
private async invokeChildren(root: string, cursor: RouteResolutionCursor, ctx: RoutingInvocationContext): Promise<void> {
const activeChildren = [...ApplicationPartUtils.scanActiveChildren(this, { includeSelf: false, nested: false })];
if (activeChildren.length !== 0) {
let flag = true;
for (const [activeRoute, activePart, child] of activeChildren) {
if (activeRoute !== ctx.data.route) {
this._application.desactivate(activePart.name);
for (const [route, part, child] of activeChildren) {
if (route !== ctx.data.route) {
this._application.desactivate(part.name);
}
else if (flag && await child.handle(root, cursor, ctx)) {
flag = false;
this._logger.debug("Child router handled the remaining route {path}", cursor.toString());
}
else if (this._application.activeChild === activePart) {
this._application.desactivate(activePart.name);
}
}

if (flag && cursor.remaining !== 0) {
Expand Down
91 changes: 91 additions & 0 deletions tests/part-activation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { assert } from "chai";
import { ApplicationPartLifecycleHooks, SinglePageApplication, ApplicationPartActivated, IApplicationPart, IContainerRouteData, ApplicationPartDeactivated, ApplicationPartSetup } from "../src";
import { resolveServiceId, ServiceContract, ServiceIdentifier } from "@aster-js/ioc";
import { IDisposable } from "@aster-js/core";

describe("SinglePageApplication", () => {

beforeEach(() => {
history.replaceState({}, "", location.origin);
});

const ICustomService = ServiceIdentifier<CustomService>("CustomService");

@ServiceContract(ICustomService)
class CustomService {
setupCount: number = 0;
activateCount: number = 0;
deactivateCount: number = 0;

@ApplicationPartSetup
load(_: IApplicationPart): Promise<void> {
this.setupCount++;
return Promise.resolve();
}

@ApplicationPartActivated
activated(_: IApplicationPart): Promise<void> {
this.activateCount++;
return Promise.resolve();
}

@ApplicationPartDeactivated
deactivated(_: IApplicationPart): Promise<void> {
this.deactivateCount++;
return Promise.resolve();
}
}

it("Should load and unload app properly", async () => {

using app = await SinglePageApplication.start("LoadTest", x => {
x.addPart("/:part<moon>", x => x.configure(x => x.addSingleton(CustomService)));
x.addPart("/:part<sun>?sun", x => x.configure(x => x.addSingleton(CustomService)));
});

const firstApp = app.activeChild!;
assert.isDefined(firstApp);
assert.equal(firstApp.name, "sun");

const firstService = firstApp.services.get(ICustomService)!;
assert.isDefined(firstService);
assert.equal(firstService.setupCount, 1);
assert.equal(firstService.activateCount, 1);
assert.equal(firstService.deactivateCount, 0);

await app.navigate("/moon");

assert.equal(firstService.setupCount, 1);
assert.equal(firstService.activateCount, 1);
assert.equal(firstService.deactivateCount, 1);

const secondApp = app.activeChild!;
assert.isDefined(secondApp);
assert.equal(secondApp.name, "moon");

const secondService = secondApp.services.get(ICustomService)!;
assert.isDefined(secondService);
assert.notEqual(firstService, secondService);
assert.equal(secondService.setupCount, 1);
assert.equal(secondService.activateCount, 1);
assert.equal(secondService.deactivateCount, 0);

await app.navigate("/sun");

assert.equal(secondService.setupCount, 1);
assert.equal(secondService.activateCount, 1);
assert.equal(secondService.deactivateCount, 1);

const thirdApp = app.activeChild!;
assert.isDefined(thirdApp);
assert.equal(thirdApp.name, "sun");

const thirdService = thirdApp.services.get(ICustomService)!;
assert.isDefined(thirdService);
assert.equal(firstService, thirdService);
assert.equal(thirdService.setupCount, 1);
assert.equal(thirdService.activateCount, 2);
assert.equal(thirdService.deactivateCount, 1);
});

});

0 comments on commit ca0d0ad

Please sign in to comment.