mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-07 16:19:01 +00:00
Compare commits
5 commits
afa64c1d0f
...
5c1f91ed6d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c1f91ed6d | ||
|
|
ce2f905464 | ||
|
|
69a92cafda | ||
|
|
c5e01c7491 | ||
| 40a3b135cf |
9 changed files with 1193 additions and 1061 deletions
25
.github/workflows/ci.yaml
vendored
25
.github/workflows/ci.yaml
vendored
|
|
@ -82,6 +82,29 @@ jobs:
|
|||
if-no-files-found: ignore
|
||||
retention-days: 14
|
||||
|
||||
browser-smoke:
|
||||
name: browser-smoke
|
||||
runs-on: ubuntu-latest
|
||||
container: mcr.microsoft.com/playwright:v1.61.1-noble
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
node-version: "24"
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run test:e2e
|
||||
- name: Upload smoke artifacts
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: browser-smoke-artifacts
|
||||
path: |
|
||||
test-results
|
||||
playwright-report
|
||||
if-no-files-found: ignore
|
||||
retention-days: 14
|
||||
|
||||
governance:
|
||||
# Repo-owned gates: godfile ratchet, changed-file maintainability,
|
||||
# Terraform fmt/validate, import-plan guard, HCP run guard, CloudFront
|
||||
|
|
@ -150,7 +173,7 @@ jobs:
|
|||
ci-complete:
|
||||
name: ci-complete
|
||||
if: always()
|
||||
needs: [static, build, unit, visual, governance]
|
||||
needs: [static, build, unit, visual, browser-smoke, governance]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: All required jobs passed
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ build (`tsc -b && vite build`), unit tests (`vitest run`), and the governance
|
|||
checks (`npm run governance`), including G13 app/Terraform isolation. **Do not
|
||||
claim a task is done until `npm run verify` is green locally.** CI runs the same
|
||||
gates as parallel jobs in [`.github/workflows/ci.yaml`](.github/workflows/ci.yaml)
|
||||
(`static`, `build`, `unit`, `visual`, `governance`) with `ci-complete` failing if
|
||||
any of those jobs did not succeed.
|
||||
(`static`, `build`, `unit`, `visual`, `browser-smoke`, `governance`) with
|
||||
`ci-complete` failing if any of those jobs did not succeed.
|
||||
|
||||
## Non-negotiable rules (enforced; do not work around)
|
||||
|
||||
|
|
|
|||
|
|
@ -63,9 +63,10 @@ isolation). A task is not done until this is green.
|
|||
- **CI ([`.github/workflows/ci.yaml`](.github/workflows/ci.yaml)):** this
|
||||
repository owns every job. `static` (`format:check` + `lint`), `build`
|
||||
(`tsc -b && vite build`), `unit` (`vitest run` in four shards), `visual`
|
||||
(Playwright visual), and `governance` (`npm run governance`, with Terraform
|
||||
1.16.0) run in parallel. `ci-complete` fails unless all of those jobs
|
||||
succeeded and is the required merge-queue check. Live G13 runs on
|
||||
(Playwright visual), `browser-smoke` (`npm run test:e2e`), and `governance`
|
||||
(`npm run governance`, with Terraform 1.16.0) run in parallel. `ci-complete`
|
||||
fails unless all of those jobs succeeded and is the required merge-queue
|
||||
check. Live G13 runs on
|
||||
`pull_request` (merge-base range), `merge_group` (each queued PR as a
|
||||
first-parent commit), and locally. It skips `push`. A Terraform-only PR
|
||||
stacked with an app-only PR still passes; a mixed change set still fails.
|
||||
|
|
|
|||
|
|
@ -150,7 +150,8 @@ No stored AWS keys — OIDC only. Infrastructure and content deploy separately:
|
|||
|
||||
- **CI** ([`.github/workflows/ci.yaml`](.github/workflows/ci.yaml)) — on push
|
||||
and PRs to `main`, runs format, lint, build, sharded unit tests, visual
|
||||
regression, and `npm run governance` as parallel jobs, then `ci-complete`.
|
||||
regression, Playwright smoke, and `npm run governance` as parallel jobs, then
|
||||
`ci-complete`.
|
||||
Conventions and gates are documented under
|
||||
[`AGENTS.md`](AGENTS.md), [`QUALITY_GATES.md`](QUALITY_GATES.md),
|
||||
[`ARCHITECTURE_AND_CODE_QUALITY.md`](ARCHITECTURE_AND_CODE_QUALITY.md), and
|
||||
|
|
|
|||
|
|
@ -50,6 +50,10 @@ function multipartBoundary(request: Request): string | undefined {
|
|||
return /^multipart\/form-data; boundary=(.+)$/.exec(contentType)?.[1];
|
||||
}
|
||||
|
||||
function multipartFileName(body: string): string | undefined {
|
||||
return /filename="([^"]+)"/.exec(body)?.[1];
|
||||
}
|
||||
|
||||
async function hideQueryDevtools(page: Page) {
|
||||
await page.addStyleTag({
|
||||
content:
|
||||
|
|
@ -141,7 +145,7 @@ test.describe("Completion uploads on a mobile viewport", () => {
|
|||
const request = route.request();
|
||||
const body = request.postDataBuffer()?.toString("latin1") ?? "";
|
||||
uploads.push({ contentType: request.headers()["content-type"] ?? "", body });
|
||||
const fileName = /filename="([^"]+)"/.exec(body)?.[1] ?? "file";
|
||||
const fileName = multipartFileName(body) ?? "file";
|
||||
const item = { id: 900 + media.length, category: 3, url: `/Assets/${fileName}`, fileName };
|
||||
media.push(item);
|
||||
return fulfillJson(route, item);
|
||||
|
|
@ -156,18 +160,18 @@ test.describe("Completion uploads on a mobile viewport", () => {
|
|||
const panel = await openCompletionTab(page, row.woNumber);
|
||||
const chooser = page.waitForEvent("filechooser");
|
||||
await panel.getByRole("button", { name: /Drag files here or click to browse/ }).click();
|
||||
await (
|
||||
await chooser
|
||||
).setFiles([
|
||||
const files = [
|
||||
{ name: "IMG_0001.jpg", mimeType: "image/jpeg", buffer: JPEG },
|
||||
{ name: "VID_0002.MP4", mimeType: "video/mp4", buffer: MP4 },
|
||||
{ name: "IMG_1587.MOV", mimeType: "video/quicktime", buffer: MOV },
|
||||
]);
|
||||
];
|
||||
await (await chooser).setFiles(files);
|
||||
|
||||
await expect.poll(() => uploads.length).toBe(3);
|
||||
for (const [index, name] of ["IMG_0001.jpg", "VID_0002.MP4", "IMG_1587.MOV"].entries()) {
|
||||
expect(uploads[index]!.contentType).toMatch(/^multipart\/form-data; boundary=/);
|
||||
expect(uploads[index]!.body).toContain(`filename="${name}"`);
|
||||
await expect.poll(() => uploads.length).toBe(files.length);
|
||||
for (const { name } of files) {
|
||||
const upload = uploads.find((item) => multipartFileName(item.body) === name);
|
||||
expect(upload, `multipart upload for ${name}`).toBeTruthy();
|
||||
expect(upload!.contentType).toMatch(/^multipart\/form-data; boundary=/);
|
||||
}
|
||||
|
||||
await panel.getByRole("combobox", { name: "Category for IMG_0001.jpg" }).selectOption("Before");
|
||||
|
|
|
|||
2117
package-lock.json
generated
2117
package-lock.json
generated
File diff suppressed because it is too large
Load diff
72
package.json
72
package.json
|
|
@ -50,63 +50,63 @@
|
|||
"dependencies": {
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@fontsource/dm-sans": "^5.2.8",
|
||||
"@fontsource/jetbrains-mono": "^5.2.8",
|
||||
"@fontsource/montserrat": "^5.2.8",
|
||||
"@fontsource/dm-sans": "^5.3.0",
|
||||
"@fontsource/jetbrains-mono": "^5.3.0",
|
||||
"@fontsource/montserrat": "^5.3.0",
|
||||
"@fullcalendar/daygrid": "^6.1.21",
|
||||
"@fullcalendar/interaction": "^6.1.21",
|
||||
"@fullcalendar/react": "^6.1.21",
|
||||
"@fullcalendar/timegrid": "^6.1.21",
|
||||
"@generouted/react-router": "^1.20.2",
|
||||
"@hookform/resolvers": "^5.4.0",
|
||||
"@mui/icons-material": "^9.2.0",
|
||||
"@mui/material": "^9.2.0",
|
||||
"@sentry/react": "10.73.0",
|
||||
"@hookform/resolvers": "^5.9.1",
|
||||
"@mui/icons-material": "^9.4.0",
|
||||
"@mui/material": "^9.4.0",
|
||||
"@sentry/react": "10.75.0",
|
||||
"@tanstack/query-broadcast-client-experimental": "5.101.2",
|
||||
"@tanstack/react-query": "5.101.2",
|
||||
"@tanstack/react-query-devtools": "5.101.2",
|
||||
"@testing-library/dom": "^10.4.1",
|
||||
"@testing-library/dom": "^10.4.2",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@testing-library/react": "^16.3.3",
|
||||
"@testing-library/user-event": "^14.6.7",
|
||||
"clsx": "^2.1.1",
|
||||
"ky": "^2.0.2",
|
||||
"lucide-react": "^1.24.0",
|
||||
"react": "^19.2.7",
|
||||
"react-dom": "^19.2.7",
|
||||
"react-hook-form": "^7.81.0",
|
||||
"ky": "^2.1.0",
|
||||
"lucide-react": "^1.47.0",
|
||||
"react": "^19.3.0",
|
||||
"react-dom": "^19.3.0",
|
||||
"react-hook-form": "^7.88.0",
|
||||
"react-icons": "^5.7.0",
|
||||
"react-router": "^8.2.0",
|
||||
"react-router": "^8.4.0",
|
||||
"react-toastify": "^11.1.0",
|
||||
"tailwind-merge": "^3.6.0",
|
||||
"zod": "^4.4.3"
|
||||
"tailwind-merge": "^3.7.0",
|
||||
"zod": "^4.6.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^21.2.1",
|
||||
"@commitlint/config-conventional": "^21.2.0",
|
||||
"@commitlint/cli": "^21.2.2",
|
||||
"@commitlint/config-conventional": "^21.2.2",
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@playwright/test": "^1.61.1",
|
||||
"@sentry/cli": "3.7.0",
|
||||
"@tailwindcss/vite": "^4.3.2",
|
||||
"@types/node": "^24.13.3",
|
||||
"@types/react": "^19.2.17",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"@vitejs/plugin-react": "^6.0.3",
|
||||
"@vitest/coverage-v8": "^4.1.10",
|
||||
"eslint": "^10.7.0",
|
||||
"@sentry/cli": "3.8.0",
|
||||
"@tailwindcss/vite": "^4.3.3",
|
||||
"@types/node": "^24.13.5",
|
||||
"@types/react": "^19.3.0",
|
||||
"@types/react-dom": "^19.3.0",
|
||||
"@vitejs/plugin-react": "^6.1.1",
|
||||
"@vitest/coverage-v8": "^4.1.11",
|
||||
"eslint": "^10.11.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-react-hooks": "^7.1.1",
|
||||
"eslint-plugin-react-refresh": "^0.5.3",
|
||||
"globals": "^17.7.0",
|
||||
"eslint-plugin-react-refresh": "^0.5.7",
|
||||
"globals": "^17.12.0",
|
||||
"husky": "^9.1.7",
|
||||
"jsdom": "^29.1.1",
|
||||
"lint-staged": "^17.0.8",
|
||||
"prettier": "^3.9.5",
|
||||
"tailwindcss": "^4.3.2",
|
||||
"lint-staged": "^17.5.1",
|
||||
"prettier": "^3.9.8",
|
||||
"tailwindcss": "^4.3.3",
|
||||
"typescript": "~6.0.3",
|
||||
"typescript-eslint": "^8.63.0",
|
||||
"vite": "^8.1.4",
|
||||
"vitest": "^4.1.10"
|
||||
"typescript-eslint": "^8.70.0",
|
||||
"vite": "^8.3.0",
|
||||
"vitest": "^4.1.11"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.22.1"
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ export default defineConfig({
|
|||
fullyParallel: true,
|
||||
forbidOnly: Boolean(process.env.CI),
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
workers: process.env.CI ? 2 : undefined,
|
||||
reporter: process.env.CI ? "github" : "list",
|
||||
use: {
|
||||
// Port 4173 isolates Playwright from the app's normal port 3000 server.
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ export default defineConfig({
|
|||
fullyParallel: false,
|
||||
forbidOnly: true,
|
||||
retries: 0,
|
||||
workers: 1,
|
||||
workers: 2,
|
||||
reporter: [["list"], ["html", { outputFolder: "playwright-report-visual", open: "never" }]],
|
||||
outputDir: "test-results/visual",
|
||||
snapshotPathTemplate: "{testDir}/__screenshots__/{testFilePath}/{arg}{ext}",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue