feat: integrate reports into Campaign navigation
This commit is contained in:
@@ -338,7 +338,7 @@ CAMPAIGN_USER_DOCUMENTATION = (
|
|||||||
order=37,
|
order=37,
|
||||||
audience=("campaign_aggregate_reader", "campaign_reader", "campaign_manager"),
|
audience=("campaign_aggregate_reader", "campaign_reader", "campaign_manager"),
|
||||||
required_scopes=("campaigns:report:read",),
|
required_scopes=("campaigns:report:read",),
|
||||||
route="/reports",
|
route="/campaigns/reports",
|
||||||
screen="Reports",
|
screen="Reports",
|
||||||
help_contexts=("campaign.report",),
|
help_contexts=("campaign.report",),
|
||||||
prerequisites=("The campaign is owned by or explicitly shared with you, or you hold tenant-wide authority.",),
|
prerequisites=("The campaign is owned by or explicitly shared with you, or you hold tenant-wide authority.",),
|
||||||
@@ -351,7 +351,7 @@ CAMPAIGN_USER_DOCUMENTATION = (
|
|||||||
outcome="A business-level Campaign outcome view with small-group and recipient privacy preserved.",
|
outcome="A business-level Campaign outcome view with small-group and recipient privacy preserved.",
|
||||||
verification="No row, address, message, attachment, diagnostic, filter, drill-down, or export action is available from the aggregate view.",
|
verification="No row, address, message, attachment, diagnostic, filter, drill-down, or export action is available from the aggregate view.",
|
||||||
related_topic_ids=("campaigns.workflow.view-delivery-report",),
|
related_topic_ids=("campaigns.workflow.view-delivery-report",),
|
||||||
links=(DocumentationLink(label="Aggregate Campaign reports", href="/reports", kind="runtime"),),
|
links=(DocumentationLink(label="Aggregate Campaign reports", href="/campaigns/reports", kind="runtime"),),
|
||||||
),
|
),
|
||||||
_workflow_topic(
|
_workflow_topic(
|
||||||
topic_id="campaigns.workflow.view-delivery-report",
|
topic_id="campaigns.workflow.view-delivery-report",
|
||||||
|
|||||||
@@ -88,6 +88,13 @@ OPERATOR_QUEUE_REQUIRED_ANY = (
|
|||||||
)
|
)
|
||||||
# Preserve the former /operator route identity for saved View projections.
|
# Preserve the former /operator route identity for saved View projections.
|
||||||
OPERATOR_QUEUE_SURFACE_ID = "campaigns.route.operator"
|
OPERATOR_QUEUE_SURFACE_ID = "campaigns.route.operator"
|
||||||
|
# Preserve the former /reports route identity for saved View projections.
|
||||||
|
REPORTS_SURFACE_ID = "campaigns.route.reports"
|
||||||
|
REPORTS_REQUIRED_ANY = ("campaigns:report:read",)
|
||||||
|
CAMPAIGN_MODULE_REQUIRED_ANY = (
|
||||||
|
"campaigns:campaign:read",
|
||||||
|
*REPORTS_REQUIRED_ANY,
|
||||||
|
)
|
||||||
|
|
||||||
ROLE_TEMPLATES = (
|
ROLE_TEMPLATES = (
|
||||||
RoleTemplate(
|
RoleTemplate(
|
||||||
@@ -230,8 +237,7 @@ manifest = ModuleManifest(
|
|||||||
role_templates=ROLE_TEMPLATES,
|
role_templates=ROLE_TEMPLATES,
|
||||||
tenant_summary_providers=(_tenant_summary,),
|
tenant_summary_providers=(_tenant_summary,),
|
||||||
nav_items=(
|
nav_items=(
|
||||||
NavItem(path="/campaigns", label="Campaigns", icon="campaign", required_any=("campaigns:campaign:read",), order=20),
|
NavItem(path="/campaigns", label="Campaigns", icon="campaign", required_any=CAMPAIGN_MODULE_REQUIRED_ANY, order=20),
|
||||||
NavItem(path="/reports", label="Reports", icon="clipboard-pen-line", required_any=("campaigns:report:read",), order=70),
|
|
||||||
),
|
),
|
||||||
frontend=FrontendModule(
|
frontend=FrontendModule(
|
||||||
module_id="campaigns",
|
module_id="campaigns",
|
||||||
@@ -239,8 +245,8 @@ manifest = ModuleManifest(
|
|||||||
routes=(
|
routes=(
|
||||||
FrontendRoute(
|
FrontendRoute(
|
||||||
path="/campaigns",
|
path="/campaigns",
|
||||||
component="CampaignListPage",
|
component="CampaignModulePage",
|
||||||
required_any=("campaigns:campaign:read",),
|
required_any=CAMPAIGN_MODULE_REQUIRED_ANY,
|
||||||
order=20,
|
order=20,
|
||||||
),
|
),
|
||||||
FrontendRoute(
|
FrontendRoute(
|
||||||
@@ -258,16 +264,16 @@ manifest = ModuleManifest(
|
|||||||
order=22,
|
order=22,
|
||||||
),
|
),
|
||||||
FrontendRoute(
|
FrontendRoute(
|
||||||
path="/reports",
|
path="/campaigns/reports",
|
||||||
component="AggregateReportsPage",
|
component="AggregateReportsPage",
|
||||||
required_any=("campaigns:report:read",),
|
required_any=REPORTS_REQUIRED_ANY,
|
||||||
order=70,
|
order=22,
|
||||||
|
surface_id=REPORTS_SURFACE_ID,
|
||||||
),
|
),
|
||||||
FrontendRoute(path="/templates", component="TemplatesPage", order=90),
|
FrontendRoute(path="/templates", component="TemplatesPage", order=90),
|
||||||
),
|
),
|
||||||
nav_items=(
|
nav_items=(
|
||||||
NavItem(path="/campaigns", label="Campaigns", icon="campaign", required_any=("campaigns:campaign:read",), order=20),
|
NavItem(path="/campaigns", label="Campaigns", icon="campaign", required_any=CAMPAIGN_MODULE_REQUIRED_ANY, order=20),
|
||||||
NavItem(path="/reports", label="Reports", icon="clipboard-pen-line", required_any=("campaigns:report:read",), order=70),
|
|
||||||
NavItem(path="/templates", label="Templates", icon="layout-template", order=90),
|
NavItem(path="/templates", label="Templates", icon="layout-template", order=90),
|
||||||
),
|
),
|
||||||
view_surfaces=(
|
view_surfaces=(
|
||||||
@@ -461,7 +467,7 @@ manifest = ModuleManifest(
|
|||||||
),
|
),
|
||||||
links=(
|
links=(
|
||||||
DocumentationLink(label="Campaign operator queue", href="/campaigns/queue", kind="runtime"),
|
DocumentationLink(label="Campaign operator queue", href="/campaigns/queue", kind="runtime"),
|
||||||
DocumentationLink(label="Campaign reports", href="/reports", kind="runtime"),
|
DocumentationLink(label="Campaign reports", href="/campaigns/reports", kind="runtime"),
|
||||||
DocumentationLink(label="Campaign delivery runbook", href="govoplan-campaign/docs/CAMPAIGN_DELIVERY_RUNBOOK.md", kind="repository"),
|
DocumentationLink(label="Campaign delivery runbook", href="govoplan-campaign/docs/CAMPAIGN_DELIVERY_RUNBOOK.md", kind="repository"),
|
||||||
),
|
),
|
||||||
related_modules=("mail", "audit"),
|
related_modules=("mail", "audit"),
|
||||||
|
|||||||
@@ -361,7 +361,7 @@ def test_aggregate_report_task_never_implies_recipient_detail_or_export_authorit
|
|||||||
item for item in CAMPAIGN_USER_DOCUMENTATION
|
item for item in CAMPAIGN_USER_DOCUMENTATION
|
||||||
if item.id == "campaigns.workflow.view-aggregate-delivery-report"
|
if item.id == "campaigns.workflow.view-aggregate-delivery-report"
|
||||||
)
|
)
|
||||||
assert topic.metadata["route"] == "/reports"
|
assert topic.metadata["route"] == "/campaigns/reports"
|
||||||
assert "export" in topic.metadata["verification"].lower()
|
assert "export" in topic.metadata["verification"].lower()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from govoplan_campaign.backend.manifest import (
|
from govoplan_campaign.backend.manifest import (
|
||||||
|
CAMPAIGN_MODULE_REQUIRED_ANY,
|
||||||
OPERATOR_QUEUE_REQUIRED_ANY,
|
OPERATOR_QUEUE_REQUIRED_ANY,
|
||||||
OPERATOR_QUEUE_SURFACE_ID,
|
OPERATOR_QUEUE_SURFACE_ID,
|
||||||
|
REPORTS_REQUIRED_ANY,
|
||||||
|
REPORTS_SURFACE_ID,
|
||||||
get_manifest,
|
get_manifest,
|
||||||
)
|
)
|
||||||
from govoplan_core.core.registry import manifest_view_surfaces
|
from govoplan_core.core.registry import manifest_view_surfaces
|
||||||
@@ -31,3 +34,31 @@ def test_operator_queue_is_an_integrated_campaign_view() -> None:
|
|||||||
]
|
]
|
||||||
assert len(queue_surfaces) == 1
|
assert len(queue_surfaces) == 1
|
||||||
assert queue_surfaces[0].description == "/campaigns/queue"
|
assert queue_surfaces[0].description == "/campaigns/queue"
|
||||||
|
|
||||||
|
|
||||||
|
def test_aggregate_reports_are_an_integrated_campaign_view() -> None:
|
||||||
|
manifest = get_manifest()
|
||||||
|
assert manifest.frontend is not None
|
||||||
|
|
||||||
|
backend_nav = {item.path: item for item in manifest.nav_items}
|
||||||
|
frontend_nav = {item.path: item for item in manifest.frontend.nav_items}
|
||||||
|
assert "/reports" not in backend_nav
|
||||||
|
assert "/reports" not in frontend_nav
|
||||||
|
assert backend_nav["/campaigns"].required_any == CAMPAIGN_MODULE_REQUIRED_ANY
|
||||||
|
assert frontend_nav["/campaigns"].required_any == CAMPAIGN_MODULE_REQUIRED_ANY
|
||||||
|
|
||||||
|
routes = {route.path: route for route in manifest.frontend.routes}
|
||||||
|
assert "/reports" not in routes
|
||||||
|
report = routes["/campaigns/reports"]
|
||||||
|
assert report.component == "AggregateReportsPage"
|
||||||
|
assert report.required_any == REPORTS_REQUIRED_ANY
|
||||||
|
assert REPORTS_SURFACE_ID == "campaigns.route.reports"
|
||||||
|
assert report.surface_id == REPORTS_SURFACE_ID
|
||||||
|
|
||||||
|
report_surfaces = [
|
||||||
|
surface
|
||||||
|
for surface in manifest_view_surfaces(manifest)
|
||||||
|
if surface.id == REPORTS_SURFACE_ID
|
||||||
|
]
|
||||||
|
assert len(report_surfaces) == 1
|
||||||
|
assert report_surfaces[0].description == "/campaigns/reports"
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ import { OPERATOR_QUEUE_ROUTE_SCOPES } from "../operator/operatorQueueAccess";
|
|||||||
|
|
||||||
const CampaignListPage = lazy(() => import("./CampaignListPage"));
|
const CampaignListPage = lazy(() => import("./CampaignListPage"));
|
||||||
const OperatorQueuePage = lazy(() => import("../operator/OperatorQueuePage"));
|
const OperatorQueuePage = lazy(() => import("../operator/OperatorQueuePage"));
|
||||||
|
const AggregateReportsPage = lazy(() => import("../reports/AggregateReportsPage"));
|
||||||
|
|
||||||
export type CampaignModuleSection = "campaigns" | "queue";
|
export type CampaignModuleSection = "campaigns" | "queue" | "reports";
|
||||||
|
|
||||||
export default function CampaignModulePage({
|
export default function CampaignModulePage({
|
||||||
active,
|
active,
|
||||||
@@ -24,24 +25,42 @@ export default function CampaignModulePage({
|
|||||||
auth: AuthInfo;
|
auth: AuthInfo;
|
||||||
}) {
|
}) {
|
||||||
const navigate = useGuardedNavigate();
|
const navigate = useGuardedNavigate();
|
||||||
|
const canReadCampaigns = hasAnyScope(auth, ["campaigns:campaign:read"]);
|
||||||
|
const canUseQueue =
|
||||||
|
canReadCampaigns && hasAnyScope(auth, OPERATOR_QUEUE_ROUTE_SCOPES);
|
||||||
|
const canReadReports = hasAnyScope(auth, ["campaigns:report:read"]);
|
||||||
const groups: ModuleSubnavGroup<CampaignModuleSection>[] = [{
|
const groups: ModuleSubnavGroup<CampaignModuleSection>[] = [{
|
||||||
items: [
|
items: [
|
||||||
{
|
...(canReadCampaigns
|
||||||
id: "campaigns",
|
? [{
|
||||||
label: "i18n:govoplan-campaign.all_campaigns.2bd1ee3a",
|
id: "campaigns" as const,
|
||||||
primary: true
|
label: "i18n:govoplan-campaign.all_campaigns.2bd1ee3a",
|
||||||
},
|
primary: true
|
||||||
...(hasAnyScope(auth, OPERATOR_QUEUE_ROUTE_SCOPES)
|
}]
|
||||||
|
: []),
|
||||||
|
...(canUseQueue
|
||||||
? [{
|
? [{
|
||||||
id: "queue" as const,
|
id: "queue" as const,
|
||||||
label: "i18n:govoplan-campaign.operator_queue.72492fb5"
|
label: "i18n:govoplan-campaign.operator_queue.72492fb5"
|
||||||
}]
|
}]
|
||||||
|
: []),
|
||||||
|
...(canReadReports
|
||||||
|
? [{
|
||||||
|
id: "reports" as const,
|
||||||
|
label: "i18n:govoplan-campaign.reports.88bc3fe3",
|
||||||
|
primary: !canReadCampaigns
|
||||||
|
}]
|
||||||
: [])
|
: [])
|
||||||
]
|
]
|
||||||
}];
|
}];
|
||||||
|
|
||||||
function select(section: CampaignModuleSection) {
|
function select(section: CampaignModuleSection) {
|
||||||
navigate(section === "queue" ? "/campaigns/queue" : "/campaigns");
|
const routes: Record<CampaignModuleSection, string> = {
|
||||||
|
campaigns: "/campaigns",
|
||||||
|
queue: "/campaigns/queue",
|
||||||
|
reports: "/campaigns/reports"
|
||||||
|
};
|
||||||
|
navigate(routes[section]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -50,7 +69,9 @@ export default function CampaignModulePage({
|
|||||||
<section className="workspace-content">
|
<section className="workspace-content">
|
||||||
{active === "queue"
|
{active === "queue"
|
||||||
? <OperatorQueuePage settings={settings} auth={auth} />
|
? <OperatorQueuePage settings={settings} auth={auth} />
|
||||||
: <CampaignListPage settings={settings} />}
|
: active === "reports"
|
||||||
|
? <AggregateReportsPage settings={settings} />
|
||||||
|
: <CampaignListPage settings={settings} />}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { createElement, lazy, useCallback } from "react";
|
|||||||
import { Navigate, useParams } from "react-router-dom";
|
import { Navigate, useParams } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
ResourceAccessBoundary,
|
ResourceAccessBoundary,
|
||||||
|
hasAnyScope,
|
||||||
type ApiSettings,
|
type ApiSettings,
|
||||||
type AuthInfo,
|
type AuthInfo,
|
||||||
type DashboardWidgetsUiCapability,
|
type DashboardWidgetsUiCapability,
|
||||||
@@ -15,12 +16,15 @@ import "./styles/campaign-workspace.css";
|
|||||||
|
|
||||||
const CampaignModulePage = lazy(() => import("./features/campaigns/CampaignModulePage"));
|
const CampaignModulePage = lazy(() => import("./features/campaigns/CampaignModulePage"));
|
||||||
const CampaignWorkspace = lazy(() => import("./features/campaigns/CampaignWorkspace"));
|
const CampaignWorkspace = lazy(() => import("./features/campaigns/CampaignWorkspace"));
|
||||||
const AggregateReportsPage = lazy(() => import("./features/reports/AggregateReportsPage"));
|
|
||||||
const TemplatesPage = lazy(() => import("./features/templates/TemplatesPage"));
|
const TemplatesPage = lazy(() => import("./features/templates/TemplatesPage"));
|
||||||
|
|
||||||
const campaignRead = ["campaigns:campaign:read"];
|
const campaignRead = ["campaigns:campaign:read"];
|
||||||
|
const reportRead = ["campaigns:report:read"];
|
||||||
|
const campaignModuleRead = [...campaignRead, ...reportRead];
|
||||||
// Preserve the former /operator route identity for saved View projections.
|
// Preserve the former /operator route identity for saved View projections.
|
||||||
const operatorQueueSurface = "campaigns.route.operator";
|
const operatorQueueSurface = "campaigns.route.operator";
|
||||||
|
// Preserve the former /reports route identity for saved View projections.
|
||||||
|
const reportsSurface = "campaigns.route.reports";
|
||||||
const translations = {
|
const translations = {
|
||||||
en: generatedTranslations.en,
|
en: generatedTranslations.en,
|
||||||
de: generatedTranslations.de
|
de: generatedTranslations.de
|
||||||
@@ -87,16 +91,16 @@ export const campaignModule: PlatformWebModule = {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
navItems: [
|
navItems: [
|
||||||
{ to: "/campaigns", label: "i18n:govoplan-campaign.campaigns.01a23a28", iconName: "campaign", anyOf: campaignRead, order: 20 },
|
{ to: "/campaigns", label: "i18n:govoplan-campaign.campaigns.01a23a28", iconName: "campaign", anyOf: campaignModuleRead, order: 20 },
|
||||||
{ to: "/reports", label: "i18n:govoplan-campaign.reports.88bc3fe3", iconName: "clipboard-pen-line", anyOf: ["campaigns:report:read"], order: 70 },
|
|
||||||
{ to: "/templates", label: "i18n:govoplan-campaign.templates.f25b700e", iconName: "layout-template", order: 90 }],
|
{ to: "/templates", label: "i18n:govoplan-campaign.templates.f25b700e", iconName: "layout-template", order: 90 }],
|
||||||
|
|
||||||
routes: [
|
routes: [
|
||||||
{ path: "/campaigns", anyOf: campaignRead, order: 20, render: ({ settings, auth }) => createElement(CampaignModulePage, { active: "campaigns", settings, auth }) },
|
{ path: "/campaigns", anyOf: campaignModuleRead, order: 20, render: ({ settings, auth }) => createElement(CampaignModuleLandingRoute, { settings, auth }) },
|
||||||
{ path: "/operator", anyOf: OPERATOR_QUEUE_ROUTE_SCOPES, allOf: campaignRead, order: 21, surfaceId: operatorQueueSurface, render: () => createElement(Navigate, { to: "/campaigns/queue", replace: true }) },
|
{ path: "/operator", anyOf: OPERATOR_QUEUE_ROUTE_SCOPES, allOf: campaignRead, order: 21, surfaceId: operatorQueueSurface, render: () => createElement(Navigate, { to: "/campaigns/queue", replace: true }) },
|
||||||
{ path: "/campaigns/queue", anyOf: OPERATOR_QUEUE_ROUTE_SCOPES, allOf: campaignRead, order: 21, surfaceId: operatorQueueSurface, render: ({ settings, auth }) => createElement(CampaignModulePage, { active: "queue", settings, auth }) },
|
{ path: "/campaigns/queue", anyOf: OPERATOR_QUEUE_ROUTE_SCOPES, allOf: campaignRead, order: 21, surfaceId: operatorQueueSurface, render: ({ settings, auth }) => createElement(CampaignModulePage, { active: "queue", settings, auth }) },
|
||||||
|
{ path: "/reports", anyOf: reportRead, order: 22, surfaceId: reportsSurface, render: () => createElement(Navigate, { to: "/campaigns/reports", replace: true }) },
|
||||||
|
{ path: "/campaigns/reports", anyOf: reportRead, order: 22, surfaceId: reportsSurface, render: ({ settings, auth }) => createElement(CampaignModulePage, { active: "reports", settings, auth }) },
|
||||||
{ path: "/campaigns/:campaignId/*", anyOf: campaignRead, order: 22, render: ({ settings, auth }) => createElement(CampaignResourceRoute, { settings, auth }) },
|
{ path: "/campaigns/:campaignId/*", anyOf: campaignRead, order: 22, render: ({ settings, auth }) => createElement(CampaignResourceRoute, { settings, auth }) },
|
||||||
{ path: "/reports", anyOf: ["campaigns:report:read"], order: 70, render: ({ settings }) => createElement(AggregateReportsPage, { settings }) },
|
|
||||||
{ path: "/templates", order: 90, render: () => createElement(TemplatesPage) }],
|
{ path: "/templates", order: 90, render: () => createElement(TemplatesPage) }],
|
||||||
uiCapabilities: {
|
uiCapabilities: {
|
||||||
"dashboard.widgets": campaignDashboardWidgets
|
"dashboard.widgets": campaignDashboardWidgets
|
||||||
@@ -104,6 +108,13 @@ export const campaignModule: PlatformWebModule = {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function CampaignModuleLandingRoute({ settings, auth }: {settings: ApiSettings;auth: AuthInfo;}) {
|
||||||
|
if (!hasAnyScope(auth, campaignRead)) {
|
||||||
|
return createElement(Navigate, { to: "/campaigns/reports", replace: true });
|
||||||
|
}
|
||||||
|
return createElement(CampaignModulePage, { active: "campaigns", settings, auth });
|
||||||
|
}
|
||||||
|
|
||||||
function CampaignResourceRoute({ settings, auth }: {settings: ApiSettings;auth: AuthInfo;}) {
|
function CampaignResourceRoute({ settings, auth }: {settings: ApiSettings;auth: AuthInfo;}) {
|
||||||
const { campaignId = "" } = useParams();
|
const { campaignId = "" } = useParams();
|
||||||
const tenantId = (auth.active_tenant ?? auth.tenant).id;
|
const tenantId = (auth.active_tenant ?? auth.tenant).id;
|
||||||
|
|||||||
@@ -67,11 +67,19 @@ assert.doesNotMatch(source, /humanize\(/);
|
|||||||
assert.match(campaignModulePage, /<ModuleSubnav/);
|
assert.match(campaignModulePage, /<ModuleSubnav/);
|
||||||
assert.match(campaignModulePage, /id: "campaigns"/);
|
assert.match(campaignModulePage, /id: "campaigns"/);
|
||||||
assert.match(campaignModulePage, /id: "queue"/);
|
assert.match(campaignModulePage, /id: "queue"/);
|
||||||
assert.match(campaignModulePage, /navigate\(section === "queue" \? "\/campaigns\/queue" : "\/campaigns"\)/);
|
assert.match(campaignModulePage, /id: "reports"/);
|
||||||
|
assert.match(campaignModulePage, /campaigns: "\/campaigns"/);
|
||||||
|
assert.match(campaignModulePage, /queue: "\/campaigns\/queue"/);
|
||||||
|
assert.match(campaignModulePage, /reports: "\/campaigns\/reports"/);
|
||||||
|
assert.match(campaignModulePage, /navigate\(routes\[section\]\)/);
|
||||||
assert.match(campaignModulePage, /active === "queue"[\s\S]*<OperatorQueuePage/);
|
assert.match(campaignModulePage, /active === "queue"[\s\S]*<OperatorQueuePage/);
|
||||||
|
assert.match(campaignModulePage, /active === "reports"[\s\S]*<AggregateReportsPage/);
|
||||||
assert.match(moduleSource, /path: "\/campaigns\/queue"/);
|
assert.match(moduleSource, /path: "\/campaigns\/queue"/);
|
||||||
assert.match(moduleSource, /path: "\/operator"[\s\S]*createElement\(Navigate, \{ to: "\/campaigns\/queue", replace: true \}\)/);
|
assert.match(moduleSource, /path: "\/operator"[\s\S]*createElement\(Navigate, \{ to: "\/campaigns\/queue", replace: true \}\)/);
|
||||||
|
assert.match(moduleSource, /path: "\/campaigns\/reports"/);
|
||||||
|
assert.match(moduleSource, /path: "\/reports"[\s\S]*createElement\(Navigate, \{ to: "\/campaigns\/reports", replace: true \}\)/);
|
||||||
assert.doesNotMatch(moduleSource, /to: "\/operator"/);
|
assert.doesNotMatch(moduleSource, /to: "\/operator"/);
|
||||||
|
assert.doesNotMatch(moduleSource, /to: "\/reports"/);
|
||||||
assert.ok(
|
assert.ok(
|
||||||
moduleSource.indexOf('{ path: "/operator"') < moduleSource.indexOf('{ path: "/campaigns/queue"'),
|
moduleSource.indexOf('{ path: "/operator"') < moduleSource.indexOf('{ path: "/campaigns/queue"'),
|
||||||
"the canonical Campaign route must replace the legacy alias in the shared view-surface catalogue"
|
"the canonical Campaign route must replace the legacy alias in the shared view-surface catalogue"
|
||||||
@@ -81,6 +89,24 @@ assert.equal(
|
|||||||
2,
|
2,
|
||||||
"the legacy redirect and canonical queue route share one configurable view surface"
|
"the legacy redirect and canonical queue route share one configurable view surface"
|
||||||
);
|
);
|
||||||
|
assert.ok(
|
||||||
|
moduleSource.indexOf('{ path: "/reports"') < moduleSource.indexOf('{ path: "/campaigns/reports"'),
|
||||||
|
"the canonical Campaign reports route must replace the legacy alias in the shared view-surface catalogue"
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
moduleSource.split("surfaceId: reportsSurface").length - 1,
|
||||||
|
2,
|
||||||
|
"the legacy redirect and canonical reports route share one configurable view surface"
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
moduleSource.split("anyOf: campaignModuleRead").length - 1,
|
||||||
|
2,
|
||||||
|
"the Campaign navigation and landing route remain available to report-only readers"
|
||||||
|
);
|
||||||
|
assert.match(
|
||||||
|
moduleSource,
|
||||||
|
/function CampaignModuleLandingRoute[\s\S]*!hasAnyScope\(auth, campaignRead\)[\s\S]*to: "\/campaigns\/reports"/
|
||||||
|
);
|
||||||
|
|
||||||
const localized = new Map([
|
const localized = new Map([
|
||||||
["i18n:govoplan-campaign.claimed.83c87884", ["Claimed", "Beansprucht"]],
|
["i18n:govoplan-campaign.claimed.83c87884", ["Claimed", "Beansprucht"]],
|
||||||
|
|||||||
Reference in New Issue
Block a user