fix(licensing): render licensing pages like any other route

An activated app that loaded a licensing URL (bookmark, browser reload, or
cy.visit in a spec) was redirected to /home as soon as the startupservice
response landed, and LicensingGuard redirected there too while
isAppActivated was still null. Both redirects meant the page only existed
for the window between the router rendering it and startup completing - so
the same link worked or failed depending on whether a key was installed.

Drop both: the guard keeps only canDeactivate, which still holds a user on
the registration page and blocks leaving a locked app. Unactivated apps are
still sent to licensing/key from any other page.
This commit is contained in:
dc committed 2026-11-06 21:36:14 +00:00
1 parent c3985b941b
commit cb3f806d6b
4 files changed
+50 -37

No files matched your search

@@ -7,7 +7,6 @@ const routes: Routes = [
{
path: ':action',
component: LicensingComponent,
canActivate: [LicensingGuard],
canDeactivate: [LicensingGuard]
}
]
+5 -33
View File
@@ -1,40 +1,15 @@
import {
ActivatedRouteSnapshot,
RouterStateSnapshot,
Router
} from '@angular/router'
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'
import { LicensingComponent } from '../licensing/licensing.component'
import { Injectable } from '@angular/core'
import { LicenceService } from '../services/licence.service'
@Injectable()
export class LicensingGuard {
constructor(
private licenceService: LicenceService,
private router: Router
) {}
canActivate(
currentRoute: ActivatedRouteSnapshot,
currentState: RouterStateSnapshot
): boolean {
if (currentState?.root.queryParams.force !== undefined) return true
if (currentState?.url.includes('licensing/update')) {
if (!!this.licenceService.isAppActivated) return true
}
if (
this.licenceService.isAppActivated.value !== null &&
this.licenceService.isAppActivated.value === false
) {
return true
} else {
this.router.navigateByUrl('/home')
return false
}
}
constructor(private licenceService: LicenceService) {}
// Leaving the registration page would strand a user who has not registered
// yet, so it stays blocked until registration completes; a locked app
// cannot be navigated away from either.
canDeactivate(
component: LicensingComponent,
currentRoute: ActivatedRouteSnapshot,
@@ -48,8 +23,5 @@ export class LicensingGuard {
if (!!this.licenceService.appLocked.value) return false
return true
if (this.licenceService.isAppActivated.value === null) return true
return !!this.licenceService.isAppActivated.value
}
}
@@ -178,3 +178,48 @@ describe('AppService - startup retry', () => {
expect(deps.licenceService.isAppActivated.value).toBeFalse()
})
})
// Licensing pages are ordinary pages: an activated app that loads one of their
// URLs directly (bookmark, browser reload, or a spec's cy.visit) must stay
// there instead of being bounced to /home once the startupservice response
// lands. Unactivated apps are still pushed to the key page from anywhere else.
describe('AppService - licensing routes', () => {
it('leaves an activated app on a licensing URL instead of redirecting to /home', async () => {
const deps = buildDeps()
deps.sasService.request.and.resolveTo(validStartupResponse())
deps.licenceService.isAppActivated.next(true)
deps.router.url = '/#/licensing/update'
const appService = buildAppService(deps)
await appService.startUpData()
expect(deps.eventService.startupDataLoaded).toHaveBeenCalled()
expect(deps.router.navigateByUrl).not.toHaveBeenCalled()
})
it('sends an unactivated app on any other page to the licence key page', async () => {
const deps = buildDeps()
deps.sasService.request.and.resolveTo(validStartupResponse())
deps.licenceService.isAppActivated.next(false)
deps.router.url = '/#/home/tables'
const appService = buildAppService(deps)
await appService.startUpData()
expect(deps.router.navigateByUrl).toHaveBeenCalledWith(
'/licensing/key?error=missing&force=true'
)
})
it('does not redirect an unactivated app that is already on a licensing page', async () => {
const deps = buildDeps()
deps.sasService.request.and.resolveTo(validStartupResponse())
deps.licenceService.isAppActivated.next(false)
deps.router.url = '/#/licensing/register'
const appService = buildAppService(deps)
await appService.startUpData()
expect(deps.router.navigateByUrl).not.toHaveBeenCalled()
})
})
-3
View File
@@ -248,9 +248,6 @@ export class AppService {
) {
this.router.navigateByUrl('/licensing/key?error=missing&force=true')
}
} else {
if (this.router.url.includes('licensing'))
this.router.navigateByUrl('/home')
}
}
}