清理旧 Portal 导航兼容(07-16-legacy-portal-navigation-compat-cleanup)
这个提交包含在:
@@ -90,8 +90,8 @@ SYNC_ENV=0 bash deploy/trialrun/deploy.sh
|
|||||||
|
|
||||||
### 1. Scope / Trigger
|
### 1. Scope / Trigger
|
||||||
|
|
||||||
- Trigger: frontend shell, navigation labels, sidebar grouping, Portal/workbench
|
- Trigger: frontend shell, navigation labels, sidebar grouping, workbench copy,
|
||||||
copy, or live smoke assertions change.
|
or live smoke assertions change.
|
||||||
- Purpose: prevent public deployment from being marked failed only because
|
- Purpose: prevent public deployment from being marked failed only because
|
||||||
smoke tests look for stale navigation copy while the page itself is healthy.
|
smoke tests look for stale navigation copy while the page itself is healthy.
|
||||||
|
|
||||||
@@ -102,45 +102,48 @@ SYNC_ENV=0 bash deploy/trialrun/deploy.sh
|
|||||||
- Shared assertions: `client/tests/e2e/helpers/smokeAssertions.ts`
|
- Shared assertions: `client/tests/e2e/helpers/smokeAssertions.ts`
|
||||||
- Workbench route: `/portal`
|
- Workbench route: `/portal`
|
||||||
- Current primary workbench nav label: `工作台`
|
- Current primary workbench nav label: `工作台`
|
||||||
- Historical nav label still allowed in mocks or older fixtures: `Portal`
|
- Stale user-facing label that must not be accepted by current mocks or smoke:
|
||||||
|
`Portal`
|
||||||
|
|
||||||
### 3. Contracts
|
### 3. Contracts
|
||||||
|
|
||||||
- `assertPortalLoaded(page)` must assert route and page content first:
|
- `assertPortalLoaded(page)` must assert route and page content first:
|
||||||
`/portal` URL and the `今日待处理` heading.
|
`/portal` URL and the `今日待处理` heading.
|
||||||
- Navigation-label assertions for the workbench link must accept both `工作台`
|
- Navigation-label assertions for the workbench link must require the exact
|
||||||
and `Portal` while older mocks still use English labels.
|
current label `工作台`; mocks and fixtures must use the same contract.
|
||||||
- Navigation assertions must be role-aware. Admin/manager smoke may assert
|
- Navigation assertions must be role-aware. Admin/manager smoke may assert
|
||||||
team/admin entries such as `团队`, but ordinary employee smoke must not
|
team/admin entries such as `团队`, but ordinary employee smoke must not
|
||||||
require links that `NavLinks` intentionally hides for that role.
|
require links that `NavLinks` intentionally hides for that role.
|
||||||
- Smoke tests must not require a copy string that is not a product contract for
|
- Smoke tests must not require a copy string that is not a product contract for
|
||||||
public availability.
|
public availability.
|
||||||
- If the product intentionally renames the primary workbench label again, update
|
- If the product intentionally renames the primary workbench label again,
|
||||||
the shared smoke helper rather than duplicating route-specific assertions in
|
update the product contract, fixtures and shared smoke helper together rather
|
||||||
each live smoke test.
|
than adding an ad hoc fallback in one live smoke test.
|
||||||
|
|
||||||
### 4. Validation & Error Matrix
|
### 4. Validation & Error Matrix
|
||||||
|
|
||||||
- Page reaches `/portal`, `今日待处理` is visible, but nav label is `工作台` ->
|
- Page reaches `/portal`, `今日待处理` is visible, but nav label is `工作台` ->
|
||||||
smoke should pass.
|
smoke should pass.
|
||||||
|
- Page reaches `/portal` and renders `Portal` as the user-facing nav label ->
|
||||||
|
smoke should fail as a stale-copy regression.
|
||||||
- Employee reaches `/portal`, role-aware navigation hides `团队`, and employee
|
- Employee reaches `/portal`, role-aware navigation hides `团队`, and employee
|
||||||
pages such as `项目` remain reachable -> smoke should pass.
|
pages such as `项目` remain reachable -> smoke should pass.
|
||||||
- Page reaches `/portal`, but `今日待处理` is missing -> smoke should fail.
|
- Page reaches `/portal`, but `今日待处理` is missing -> smoke should fail.
|
||||||
- Page stays on login, `/403`, error fallback or another route -> smoke should
|
- Page stays on login, `/403`, error fallback or another route -> smoke should
|
||||||
fail.
|
fail.
|
||||||
- New shell label appears only in one live smoke test -> move the compatibility
|
- New shell label appears only in one live smoke test -> stop and align the
|
||||||
logic into `smokeAssertions.ts`.
|
product contract, fixtures and shared helper before changing smoke.
|
||||||
|
|
||||||
### 5. Good/Base/Bad Cases
|
### 5. Good/Base/Bad Cases
|
||||||
|
|
||||||
- Good: `assertPortalLoaded` checks `/portal`, `今日待处理`, and a workbench link
|
- Good: `assertPortalLoaded` checks `/portal`, `今日待处理`, and an exact
|
||||||
matching either `工作台` or `Portal`.
|
`工作台` workbench link.
|
||||||
- Good: `assertMainNavVisible(page, { expectTeam: false })` is used for
|
- Good: `assertMainNavVisible(page, { expectTeam: false })` is used for
|
||||||
ordinary employee live smoke.
|
ordinary employee live smoke.
|
||||||
- Base: mocked E2E fixtures may still emit `Portal`; live public navigation may
|
- Base: the internal route and helper name may retain `portal`, while all
|
||||||
emit `工作台`.
|
user-facing navigation fixtures and assertions use `工作台`.
|
||||||
- Bad: live smoke hard-codes only `Portal` and blocks deployment after a valid
|
- Bad: a mock emits `Portal` and smoke accepts it as a valid current label,
|
||||||
Chinese navigation rollout.
|
masking a user-facing English-copy regression.
|
||||||
- Bad: employee live smoke requires the `团队` sidebar link after role-aware
|
- Bad: employee live smoke requires the `团队` sidebar link after role-aware
|
||||||
navigation intentionally hides it.
|
navigation intentionally hides it.
|
||||||
|
|
||||||
@@ -148,6 +151,8 @@ SYNC_ENV=0 bash deploy/trialrun/deploy.sh
|
|||||||
|
|
||||||
- After changing live smoke assertions, run:
|
- After changing live smoke assertions, run:
|
||||||
`cd client && npm run typecheck`
|
`cd client && npm run typecheck`
|
||||||
|
- Load the focused visual and shell specs with Playwright `--list` when changing
|
||||||
|
fixture/report contracts.
|
||||||
- For public deploy verification, run live smoke or the readiness wrapper:
|
- For public deploy verification, run live smoke or the readiness wrapper:
|
||||||
`LIVE_SMOKE_BASE_URL=https://oa.hyinda.com bash scripts/qa/run-live-smoke-readiness.sh`
|
`LIVE_SMOKE_BASE_URL=https://oa.hyinda.com bash scripts/qa/run-live-smoke-readiness.sh`
|
||||||
- When changing the shell or Portal page, include a focused Playwright check
|
- When changing the shell or Portal page, include a focused Playwright check
|
||||||
@@ -158,17 +163,17 @@ SYNC_ENV=0 bash deploy/trialrun/deploy.sh
|
|||||||
#### Wrong
|
#### Wrong
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
await expect(page.getByRole("link", { name: "Portal" }).first()).toBeVisible();
|
await expect(
|
||||||
|
page.getByRole("link", { name: "工作台", exact: true })
|
||||||
|
.or(page.getByRole("link", { name: "Portal", exact: true }))
|
||||||
|
.first(),
|
||||||
|
).toBeVisible();
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Correct
|
#### Correct
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
await expect(
|
await expect(page.getByRole("link", { name: "工作台", exact: true }).first()).toBeVisible();
|
||||||
page.getByRole("link", { name: "工作台", exact: true })
|
|
||||||
.or(page.getByRole("link", { name: "Portal", exact: true }))
|
|
||||||
.first(),
|
|
||||||
).toBeVisible();
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Scenario: Trial Run Runtime Navigation Baseline
|
## Scenario: Trial Run Runtime Navigation Baseline
|
||||||
|
|||||||
@@ -250,7 +250,6 @@ async function readMetrics(page: Page) {
|
|||||||
clippedControls,
|
clippedControls,
|
||||||
forbiddenLike: forbiddenPage,
|
forbiddenLike: forbiddenPage,
|
||||||
appErrorLike: text.includes("页面加载失败") || text.includes("加载失败") || text.includes("出错了"),
|
appErrorLike: text.includes("页面加载失败") || text.includes("加载失败") || text.includes("出错了"),
|
||||||
navPortalCopy: text.includes("Portal"),
|
|
||||||
};
|
};
|
||||||
}, ENGLISH_PRIMARY_TERMS);
|
}, ENGLISH_PRIMARY_TERMS);
|
||||||
}
|
}
|
||||||
@@ -413,7 +412,6 @@ async function auditRoute(page: Page, plan: RoutePlan) {
|
|||||||
clippedCount: metrics.clippedControls.length,
|
clippedCount: metrics.clippedControls.length,
|
||||||
forbiddenLike: metrics.forbiddenLike,
|
forbiddenLike: metrics.forbiddenLike,
|
||||||
appErrorLike: metrics.appErrorLike,
|
appErrorLike: metrics.appErrorLike,
|
||||||
navPortalCopy: metrics.navPortalCopy,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
在新工单中引用
屏蔽一个用户