Owners and admins turn it on in Configuración → General, pick what it
shows (tickets per status, the sprint under way, release forecasts,
open epics and activity per day) and can renew or drop the link. Whoever
has it sees /share/{token} without an account: only those blocks, no
people nor single tickets but the epics, no links into the app, read
again every minute. A renewed or dropped link says it no longer works.
REST (GET/PUT/DELETE /api/projects/{project}/share and the public
GET /api/shared/dashboards/{token}) and GraphQL (dashboardShare,
shareDashboard, unshareDashboard, sharedDashboard). The token is kept
in dashboard_shares and goes with the project.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
76 lines
2.3 KiB
TypeScript
76 lines
2.3 KiB
TypeScript
import { Route } from '@angular/router';
|
|
import { authGuard } from '@task-stream/web/identity/presenters';
|
|
import { appRoutes } from './app.routes';
|
|
|
|
/** The tracker's routes, behind the lazy `''` route (and its providers' parent route). */
|
|
async function trackerChildren(): Promise<Route[]> {
|
|
const lazy = appRoutes.find(
|
|
(route) => route.path === '' && route.loadChildren,
|
|
);
|
|
const loaded = (await lazy?.loadChildren?.()) as Route[];
|
|
expect(loaded[0].providers?.length).toBeGreaterThan(0);
|
|
return loaded[0].children ?? [];
|
|
}
|
|
|
|
describe('appRoutes', () => {
|
|
it('keeps home behind the auth guard, and public projects and «Explorar» open', async () => {
|
|
const paths = appRoutes.map((route) => route.path);
|
|
expect(paths).toEqual(
|
|
expect.arrayContaining(['login', 'auth/callback', '', '**']),
|
|
);
|
|
|
|
const children = await trackerChildren();
|
|
expect(children.map((route) => route.path)).toEqual([
|
|
'',
|
|
'portfolio',
|
|
'explore',
|
|
'share/:token',
|
|
'cartera',
|
|
'explorar',
|
|
'p/:key',
|
|
]);
|
|
expect(children[0].canActivate).toContain(authGuard);
|
|
expect(children[1].canActivate).toContain(authGuard);
|
|
expect(children[2].canActivate).toBeUndefined();
|
|
// The page for clients needs no session: its link is the key.
|
|
expect(children[3].canActivate).toBeUndefined();
|
|
expect(children[6].canActivate).toBeUndefined();
|
|
// The Spanish paths of shared links lead to the English ones.
|
|
expect(children[4].redirectTo).toBe('portfolio');
|
|
expect(children[5].redirectTo).toBe('explore');
|
|
});
|
|
|
|
it('gives each page its own state, not the root', async () => {
|
|
const children = await trackerChildren();
|
|
expect(children[0].providers?.length).toBe(4);
|
|
expect(children[2].providers?.length).toBe(1);
|
|
});
|
|
|
|
it('exposes every project section', async () => {
|
|
const project = (await trackerChildren()).find(
|
|
(route) => route.path === 'p/:key',
|
|
);
|
|
expect(project?.children?.map((route) => route.path)).toEqual([
|
|
'',
|
|
'proponer',
|
|
'board',
|
|
'propose',
|
|
'list',
|
|
'issues/:issueKey',
|
|
'backlog',
|
|
'sprints',
|
|
'calendar',
|
|
'roadmap',
|
|
'epics',
|
|
'trash',
|
|
'releases',
|
|
'report/:kind/:id',
|
|
'dashboard',
|
|
'analytics',
|
|
'settings',
|
|
'activity',
|
|
'members',
|
|
]);
|
|
});
|
|
});
|