Skip to content

VIBE-323 Add Welsh translations for Care Standards Tribunal Weekly Hearing List - #205

Merged
KianKwa merged 14 commits into
masterfrom
feature/VIBE-323
Feb 13, 2026
Merged

VIBE-323 Add Welsh translations for Care Standards Tribunal Weekly Hearing List#205
KianKwa merged 14 commits into
masterfrom
feature/VIBE-323

Conversation

@KianKwa

@KianKwa KianKwa commented Jan 16, 2026

Copy link
Copy Markdown
Contributor

Jira link

https://tools.hmcts.net/jira/browse/VIBE-323

Change description

Add Welsh translations for Care Standards Tribunal Weekly Hearing List

Checklist

  • commit messages are meaningful and follow good commit message guidelines
  • README and other documentation has been updated / added (if needed)
  • tests have been updated / new tests has been added (if needed)
  • Does this PR introduce a breaking change

Summary by CodeRabbit

  • New Features

    • Added comprehensive Welsh language support for the Care Standards Tribunal Weekly Hearing List.
    • Enhanced header layout displaying week commencing date, last updated time, and linked reference information.
    • Details section now opens by default for improved accessibility.
  • Improvements

    • Refined visual hierarchy with adjusted heading and text sizing in search section.
  • Chores

    • Updated dependency versions.

@coderabbitai

coderabbitai Bot commented Jan 16, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

The PR refactors the Care Standards Tribunal weekly hearing list to enhance header display with improved date/time formatting, introduces Welsh language translations replacing placeholders, updates template styling, and expands the page renderer to support dynamic provenance label localization.

Changes

Cohort / File(s) Summary
E2E Tests
e2e-tests/tests/care-standards-tribunal-upload.spec.ts
Refactored upload flow function to navigate via publications summary instead of extracting artefactId; expanded test coverage to include list page validation, Welsh language paths, data source text verification, and table structure accessibility checks.
Template & Styling
libs/list-types/care-standards-tribunal-weekly-hearing-list/src/pages/care-standards-tribunal-weekly-hearing-list.njk
Replaced header duration/lastUpdated sections with fact link display; introduced week-commencing and last-updated date/time formatting; adjusted heading sizes and data source text styling.
Localization
libs/list-types/care-standards-tribunal-weekly-hearing-list/src/pages/en.ts, cy.ts
Added new keys (listForWeekCommencing, lastUpdated, at, factLinkText, factLinkUrl, factAdditionalText) and provenanceLabels mapping; replaced Welsh placeholders with complete Welsh translations for titles, headers, and table content.
Page Logic
libs/list-types/care-standards-tribunal-weekly-hearing-list/src/pages/index.ts, index.test.ts
Integrated listTitle into render data; switched provenance label lookup from static reference to dynamic translation-based lookup with fallback.
Rendering
libs/list-types/care-standards-tribunal-weekly-hearing-list/src/rendering/renderer.ts, renderer.test.ts
Updated header structure to include listTitle and separate weekCommencingDate, lastUpdatedDate, lastUpdatedTime fields; adjusted test expectations to assert new date/time formatting.
Dependencies
package.json
Updated qs dependency pinning from 6.14.1 to 6.14.2.

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title describes adding Welsh translations for Care Standards Tribunal, but the changeset encompasses far broader work including multiple new list-type modules, common utilities, e2e tests, and documentation—far beyond Welsh translations alone. Update the title to reflect the full scope, such as 'VIBE-323 Add Welsh translations and implement RCJ hearing list modules' or break into multiple focused PRs.
Docstring Coverage ⚠️ Warning Docstring coverage is 72.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/VIBE-323

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 20

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
e2e-tests/tests/care-standards-tribunal-upload.spec.ts (1)

268-281: Test navigates to hardcoded artefactId, ignoring uploaded artefact.

completeCSTUploadFlow returns the actual artefactId, but line 275 navigates to artefactId=test. This test will not verify the uploaded content and may fail or produce false positives.

Proposed fix
 test("should display the published CST list with correct formatting", async ({ page }) => {
   // Upload and publish the list
-  await completeCSTUploadFlow(page);
+  const artefactId = await completeCSTUploadFlow(page);

   // Navigate to the published list
-  // Note: In a real scenario, you'd navigate via search or direct URL
-  // For now, we'll construct the URL pattern
-  await page.goto("/care-standards-tribunal-weekly-hearing-list?artefactId=test");
-  await page.waitForTimeout(1000);
+  await navigateToPublishedList(page, artefactId);

   // Verify page loads (may need adjustment based on actual implementation)
   const heading = page.locator("h1");
   await expect(heading).toBeVisible();
 });
libs/list-types/common/src/mock-list-types.ts (1)

85-91: Replace the Welsh placeholder with the authoritative translation.

This list type is user-facing with dedicated Welsh localisation support. Replace welshFriendlyName with "Rhestr Gwrandawiadau Wythnosol y Tribiwnlys Safonau Gofal" (from the verified translation in libs/list-types/care-standards-tribunal-weekly-hearing-list/src/pages/cy.ts).

libs/list-types/common/package.json (1)

14-26: Ensure assets are copied into dist for production exports.

The new export map points production consumers to ./dist/assets/**, but the build only copies views. Unless another pipeline step handles assets, these exports will resolve to missing files.

Suggested fix
   "scripts": {
-    "build": "tsc && yarn build:nunjucks",
+    "build": "tsc && yarn build:nunjucks && yarn build:assets",
+    "build:assets": "mkdir -p dist/assets && cp -R src/assets/* dist/assets/",
     "build:nunjucks": "mkdir -p dist/views && cd src/views && find . -name '*.njk' -exec sh -c 'mkdir -p ../../dist/views/$(dirname {}) && cp {} ../../dist/views/{}' \\; || true",
🟡 Minor comments (17)
libs/list-types/care-standards-tribunal-weekly-hearing-list/src/conversion/cst-config.ts-17-17 (1)

17-17: Localise the validation hint message.
Line 17’s English-only hint string is user-facing; please route it through the Welsh translation mechanism (or provide a bilingual string) to meet the bilingual requirement. Based on learnings, Welsh translations are required for all user-facing text.

libs/list-types/london-administrative-court-daily-cause-list/src/schemas/london-administrative-court-daily-cause-list.json-22-25 (1)

22-25: Tighten time validation to reject invalid hours/minutes and allow AM/PM case.

Current pattern accepts values like 19pm or 9:99am and rejects AM/PM. Consider a stricter 12‑hour pattern with optional minutes.

💡 Suggested regex update
-            "pattern": "^\\d{1,2}([:.]\\d{2})?[ap]m\\s*$"
+            "pattern": "^(1[0-2]|0?[1-9])([:.][0-5]\\d)?\\s*[aApP][mM]\\s*$"

Also applies to: 59-62

libs/list-types/rcj-standard-daily-cause-list/src/pages/kings-bench-masters-daily-cause-list.njk-10-12 (1)

10-12: Guard the fact-link block when data is optional.

If common.factLinkUrl or common.factLinkText is missing, this renders an empty link. Consider rendering this block only when both values are present.

💡 Proposed fix
-    <p class="govuk-body">
-      <a href="{{ common.factLinkUrl }}" class="govuk-link">{{ common.factLinkText }}</a> {{ common.factAdditionalText }}
-    </p>
+    {% if common.factLinkUrl and common.factLinkText %}
+      <p class="govuk-body">
+        <a href="{{ common.factLinkUrl }}" class="govuk-link">{{ common.factLinkText }}</a> {{ common.factAdditionalText }}
+      </p>
+    {% endif %}
libs/list-types/rcj-standard-daily-cause-list/src/pages/county-court-central-london-civil-daily-cause-list.njk-10-12 (1)

10-12: Guard the optional fact link output.

If common.factLinkUrl or common.factLinkText is absent, this will render an empty/undefined link. Wrap the anchor in a conditional while keeping the additional text.

Proposed fix
 <p class="govuk-body">
-  <a href="{{ common.factLinkUrl }}" class="govuk-link">{{ common.factLinkText }}</a> {{ common.factAdditionalText }}
+  {% if common.factLinkUrl and common.factLinkText %}
+    <a href="{{ common.factLinkUrl }}" class="govuk-link">{{ common.factLinkText }}</a>
+  {% endif %}
+  {{ common.factAdditionalText }}
 </p>
libs/list-types/rcj-standard-daily-cause-list/README.md-20-20 (1)

20-20: Minor: "Court room" should be "Courtroom".

Static analysis flagged this as a compound noun.

📝 Suggested fix
-- **Venue** (required): Court room or location
+- **Venue** (required): Courtroom or location
libs/list-types/rcj-standard-daily-cause-list/README.md-47-50 (1)

47-50: Minor: Add language specifier to fenced code block.

Markdownlint flags code blocks without a language. Use text for plain URL patterns.

📝 Suggested fix
-```
+```text
 /civil-courts-rcj-daily-cause-list?artefactId=<id>
 /court-of-appeal-criminal-division-daily-cause-list?artefactId=<id>
 ```
libs/list-types/london-administrative-court-daily-cause-list/README.md-20-20 (1)

20-20: Minor typo: "Court room" → "Courtroom".

The noun "Courtroom" is typically written as one word.

📝 Suggested fix
-- **Venue** (required): Court room or location
+- **Venue** (required): Courtroom or location
libs/list-types/rcj-standard-daily-cause-list/src/pages/en.ts-117-125 (1)

117-125: Minor capitalisation inconsistency in table headers.

The table headers use sentence case (Case number, Case details, Hearing type, Additional information), whilst other list type files use title case (Case Number, Case Details, Hearing Type, Additional Information). Consider aligning for UI consistency.

Suggested fix
     tableHeaders: {
       venue: "Venue",
       judge: "Judge",
       time: "Time",
-      caseNumber: "Case number",
-      caseDetails: "Case details",
-      hearingType: "Hearing type",
-      additionalInformation: "Additional information"
+      caseNumber: "Case Number",
+      caseDetails: "Case Details",
+      hearingType: "Hearing Type",
+      additionalInformation: "Additional Information"
     },
docs/tickets/VIBE-317/specification.md-99-103 (1)

99-103: Replace bare URL with a Markdown link.

This avoids the MD034 lint failure.

Suggested fix
-- **URL:** https://www.find-court-tribunal.service.gov.uk/
+- **URL:** [find-court-tribunal.service.gov.uk](https://www.find-court-tribunal.service.gov.uk/)
docs/tickets/VIBE-317/implementation-summary.md-118-121 (1)

118-121: Replace bare URL with a Markdown link.

This avoids MD034.

Suggested fix
-- FaCT link (https://www.find-court-tribunal.service.gov.uk/)
+- FaCT link ([find-court-tribunal.service.gov.uk](https://www.find-court-tribunal.service.gov.uk/))
docs/tickets/VIBE-317/implementation-progress.md-235-253 (1)

235-253: Add a language to the fenced code block.

This resolves MD040 and improves readability.

Suggested fix
-```
+```text
 module-name/
 ├── package.json
 ├── tsconfig.json
 ├── README.md
 └── src/
     ├── config.ts (module exports)
     ├── index.ts (business logic exports)
     ├── models/types.ts
     ├── conversion/config.ts
     ├── schemas/*.json
     ├── validation/json-validator.ts + tests
     ├── rendering/renderer.ts + tests
     └── pages/
         ├── index.ts (GET handler)
         ├── template.njk
         ├── en.ts
         └── cy.ts
-```
+```
libs/list-types/london-administrative-court-daily-cause-list/src/pages/cy.ts-6-8 (1)

6-8: Translate remaining English place name in the Welsh locale.

locationLine2 uses “Strand, London”, leaving “London” in English. Please translate/confirm the Welsh form (e.g., “Strand, Llundain”) to keep all user-facing text in Welsh.

Based on learnings, Welsh translations are required for all user-facing text.

libs/admin-pages/src/pages/non-strategic-upload-summary/index.ts-93-99 (1)

93-99: Make Excel detection case‑insensitive and avoid conversion when filename is missing.
Line 98 treats “.JSON” as Excel and also marks missing filenames as Excel. Consider normalising and guarding nulls to prevent unintended conversion.

🛠️ Proposed fix
-    const isExcelFile = !uploadData.fileName?.endsWith(".json");
+    const fileName = uploadData.fileName?.toLowerCase() ?? "";
+    const isExcelFile = fileName !== "" && !fileName.endsWith(".json");

Also applies to: 121-121

libs/list-types/court-of-appeal-civil-daily-cause-list/README.md-49-50 (1)

49-50: Refresh date examples to the current year.

The examples still use 2025; updating to 2026 avoids confusion for readers in the current year.

Proposed update
-- **Tab 2**: Date must be dd/MM/yyyy format (e.g., 15/01/2025), Time must be HH:MM format
+- **Tab 2**: Date must be dd/MM/yyyy format (e.g., 15/01/2026), Time must be HH:MM format
@@
-- Dates are formatted according to locale (English: "15 January 2025", Welsh: "15 Ionawr 2025")
+- Dates are formatted according to locale (English: "15 January 2026", Welsh: "15 Ionawr 2026")

Also applies to: 60-60

libs/list-types/court-of-appeal-civil-daily-cause-list/README.md-75-77 (1)

75-77: Specify a language on the fenced block.

This fixes the markdownlint MD040 warning.

Proposed fix
-```
+```text
 /court-of-appeal-civil-division-daily-cause-list?artefactId=<id>
</details>

</blockquote></details>
<details>
<summary>libs/list-types/court-of-appeal-civil-daily-cause-list/src/pages/index.ts-77-88 (1)</summary><blockquote>

`77-88`: **`JSON.parse` failure would be caught as 500, not 400.**

If the file contains malformed JSON, `JSON.parse` throws a `SyntaxError` that will be caught by the outer `catch` block and return a 500 error. Consider wrapping the parse in its own try-catch to return 400 with a more descriptive message.

<details>
<summary>🐛 Proposed fix</summary>

```diff
-    const jsonData: CourtOfAppealCivilData = JSON.parse(jsonContent);
+    let jsonData: CourtOfAppealCivilData;
+    try {
+      jsonData = JSON.parse(jsonContent);
+    } catch {
+      return res.status(400).render("errors/common", {
+        en,
+        cy,
+        errorTitle: "Invalid Data",
+        errorMessage: "The list data could not be parsed"
+      });
+    }
libs/list-types/administrative-court-daily-cause-list/src/pages/index.ts-36-39 (1)

36-39: Normalise locale before indexing list config

If res.locals.locale is something like en-GB, listConfig[locale] will be undefined. Consider coercing to "en"/"cy" once.

♻️ Suggested change
-  const locale = res.locals.locale || "en";
-  const t = locale === "cy" ? cy : en;
+  const locale = res.locals.locale === "cy" ? "cy" : "en";
+  const t = locale === "cy" ? cy : en;
@@
-    const listTitle = listConfig[locale as "en" | "cy"];
+    const listTitle = listConfig[locale];

Also applies to: 105-107

🧹 Nitpick comments (52)
libs/simple-router/src/simple-router.ts (1)

108-113: Validation is good; consider explicit interface typing.

The runtime validation is sound. However, the RouteModule interface (lines 185-188) relies on the index signature [key: string]: unknown to permit ROUTES. Adding an explicit optional property would improve type safety and discoverability.

♻️ Suggested interface update
 export interface RouteModule {
   [key: string]: unknown;
   onError?: ErrorRequestHandler;
+  ROUTES?: string[];
 }
libs/list-types/london-administrative-court-daily-cause-list/src/schemas/london-administrative-court-daily-cause-list.json (1)

8-80: Reduce duplication with a shared item definition.

Both arrays repeat the same object schema; using definitions + $ref will make updates safer and more consistent.

♻️ Suggested refactor
 {
   "$schema": "http://json-schema.org/draft-07/schema#",
   "title": "London Administrative Court Daily Cause List",
   "description": "Schema for London Administrative Court with Main hearings and Planning Court tabs",
   "type": "object",
   "required": ["mainHearings", "planningCourt"],
+  "definitions": {
+    "hearingItem": {
+      "type": "object",
+      "required": ["venue", "judge", "time", "caseNumber", "caseDetails", "hearingType"],
+      "properties": {
+        "venue": { "type": "string", "pattern": "^(?!(.|\\r|\\n)*<[^>]+>)(.|\\r|\\n)*$" },
+        "judge": { "type": "string", "pattern": "^(?!(.|\\r|\\n)*<[^>]+>)(.|\\r|\\n)*$" },
+        "time": { "type": "string", "pattern": "^(1[0-2]|0?[1-9])([:.][0-5]\\d)?\\s*[aApP][mM]\\s*$" },
+        "caseNumber": { "type": "string", "pattern": "^(?!(.|\\r|\\n)*<[^>]+>)(.|\\r|\\n)*$" },
+        "caseDetails": { "type": "string", "pattern": "^(?!(.|\\r|\\n)*<[^>]+>)(.|\\r|\\n)*$" },
+        "hearingType": { "type": "string", "pattern": "^(?!(.|\\r|\\n)*<[^>]+>)(.|\\r|\\n)*$" },
+        "additionalInformation": { "type": "string", "pattern": "^(?!(.|\\r|\\n)*<[^>]+>)(.|\\r|\\n)*$" }
+      }
+    }
+  },
   "properties": {
     "mainHearings": {
       "type": "array",
-      "items": { ... }
+      "items": { "$ref": "#/definitions/hearingItem" }
     },
     "planningCourt": {
       "type": "array",
-      "items": { ... }
+      "items": { "$ref": "#/definitions/hearingItem" }
     }
   }
 }
libs/list-types/rcj-standard-daily-cause-list/src/pages/family-division-high-court-daily-cause-list.njk (1)

21-24: Consider refactoring the long HTML concatenation for maintainability.

The inline string concatenation is dense and brittle. A small refactor to build the content with standard Nunjucks markup (e.g., using {% set %} blocks or partials) will improve readability and reduce future editing errors.

e2e-tests/tests/care-standards-tribunal-upload.spec.ts (6)

14-18: Add guard for missing environment variables.

Using non-null assertions without validation will produce cryptic errors if the environment variables are unset. Consider adding explicit checks.

Suggested improvement
  if (page.url().includes("login.microsoftonline.com")) {
-   const systemAdminEmail = process.env.SSO_TEST_SYSTEM_ADMIN_EMAIL!;
-   const systemAdminPassword = process.env.SSO_TEST_SYSTEM_ADMIN_PASSWORD!;
+   const systemAdminEmail = process.env.SSO_TEST_SYSTEM_ADMIN_EMAIL;
+   const systemAdminPassword = process.env.SSO_TEST_SYSTEM_ADMIN_PASSWORD;
+   if (!systemAdminEmail || !systemAdminPassword) {
+     throw new Error("SSO_TEST_SYSTEM_ADMIN_EMAIL and SSO_TEST_SYSTEM_ADMIN_PASSWORD must be set");
+   }
    await loginWithSSO(page, systemAdminEmail, systemAdminPassword);
  }

166-198: Avoid waitForTimeout in favour of deterministic waits.

waitForTimeout(1000) on line 168 introduces flakiness. Use waitForSelector, waitForLoadState, or similar Playwright APIs that wait for specific conditions.

Additionally, per coding guidelines, prefer getByLabel() or getByRole() over attribute selectors for form inputs where possible.

Example fix for waitForTimeout
  await page.goto("/non-strategic-upload?locationId=9001");
- await page.waitForTimeout(1000);
+ await page.waitForLoadState("domcontentloaded");

208-224: Consider using waitForSelector instead of waitForTimeout.

Line 210 uses a fixed delay. Replace with a condition-based wait to improve reliability.

Suggested fix
  // Navigate to summary of publications to find the artefactId
  await page.goto("/summary-of-publications");
- await page.waitForTimeout(1000);
+ await page.waitForSelector('.govuk-list a[href*="care-standards-tribunal-weekly-hearing-list?artefactId="]');

226-230: Replace waitForTimeout with condition-based wait.

Line 229 uses a fixed delay after navigation.

Suggested fix
 async function navigateToPublishedList(page: Page, artefactId: string) {
   await page.goto(`/care-standards-tribunal-weekly-hearing-list?artefactId=${artefactId}`);
-  await page.waitForTimeout(1000);
+  await page.waitForLoadState("domcontentloaded");
 }

364-374: Consider extracting repeated loop pattern to a helper.

The loop pattern to find "Last updated" text is duplicated in the Welsh language test (lines 617-627). Extract to a reusable helper for maintainability.

Example helper
async function findParagraphContaining(page: Page, searchText: string): Promise<string | null> {
  const paragraphs = page.locator(".govuk-body");
  for (let i = 0; i < await paragraphs.count(); i++) {
    const text = await paragraphs.nth(i).textContent();
    if (text?.includes(searchText)) {
      return text;
    }
  }
  return null;
}

501-507: Search highlighting assertion could be more specific.

The comment states search "highlights, doesn't hide", but the test only verifies row count and text content. Consider asserting the presence of highlight markup (e.g., <mark> elements) if highlighting is implemented.

libs/list-types/rcj-standard-daily-cause-list/src/pages/kings-bench-division-daily-cause-list.njk (1)

21-24: Avoid long HTML string concatenation; prefer a Nunjucks block with escaping.

The current html: value is built via concatenation, which is hard to read and bypasses escaping. If any listContent.* ever comes from non‑static sources, this becomes an XSS risk. Consider constructing a block with explicit | escape per paragraph and pass that as html. Please confirm all listContent.* are static translations.

♻️ Example refactor
+{% set importantInfoHtml %}
+  <h3 class="govuk-heading-s govuk-!-margin-bottom-2">{{ listContent.remoteHearingsTitle | escape }}</h3>
+  {% for p in listContent.remoteHearingsText.split('\n\n') %}
+    <p class="govuk-body">{{ p | escape }}</p>
+  {% endfor %}
+  <h3 class="govuk-heading-s govuk-!-margin-top-6 govuk-!-margin-bottom-2">{{ listContent.remoteJudgmentsTitle | escape }}</h3>
+  {% for p in listContent.remoteJudgmentsText.split('\n\n') %}
+    <p class="govuk-body">{{ p | escape }}</p>
+  {% endfor %}
+  <h3 class="govuk-heading-s govuk-!-margin-top-6 govuk-!-margin-bottom-2">{{ listContent.bundlesTitle | escape }}</h3>
+  <p class="govuk-body">{{ listContent.bundleFilingText | escape }}</p>
+{% endset %}
+
 {{ govukDetails({
   summaryText: common.importantInfoTitle,
-  html: '<h3 class="govuk-heading-s govuk-!-margin-bottom-2">' + listContent.remoteHearingsTitle + '</h3><p class="govuk-body">' + listContent.remoteHearingsText.split('\n\n').join('</p><p class="govuk-body">') + '</p><h3 class="govuk-heading-s govuk-!-margin-top-6 govuk-!-margin-bottom-2">' + listContent.remoteJudgmentsTitle + '</h3><p class="govuk-body">' + listContent.remoteJudgmentsText.split('\n\n').join('</p><p class="govuk-body">') + '</p><h3 class="govuk-heading-s govuk-!-margin-top-6 govuk-!-margin-bottom-2">' + listContent.bundlesTitle + '</h3><p class="govuk-body">' + listContent.bundleFilingText + '</p>',
+  html: importantInfoHtml,
   open: true
 }) }}
libs/list-types/rcj-standard-daily-cause-list/src/pages/county-court-central-london-civil-daily-cause-list.njk (1)

36-62: Use the govukTable macro for the hearings table.

The table is hand-built, but coding guidelines require GOV.UK Frontend component macros throughout these templates. Switch to govukTable for consistency and to follow the established pattern used in similar list-type pages.

libs/list-types/common/src/config.ts (1)

7-8: Add missing standard config exports.

This config still omits pageRoutes/apiRoutes/prismaSchemas, which the standard config contract expects. Consider exporting empty placeholders if not applicable. As per coding guidelines, please align with the required config interface.

libs/list-types/care-standards-tribunal-weekly-hearing-list/src/config.ts (1)

7-12: Add missing standard config exports.

apiRoutes and prismaSchemas are still missing from this config. Please export them (even as empty stubs) to match the standard interface. As per coding guidelines, please align with the required config interface.

libs/list-types/care-standards-tribunal-weekly-hearing-list/src/config.test.ts (1)

81-83: Use path.relative to avoid false-positive prefix matches.
startsWith can pass for /foo/bar vs /foo/barley. Prefer a relative-path check.

♻️ Proposed update
-    it("assets should be subdirectory of moduleRoot", () => {
-      expect(assets.startsWith(moduleRoot)).toBe(true);
-    });
+    it("assets should be subdirectory of moduleRoot", () => {
+      const relativeAssets = path.relative(moduleRoot, assets);
+      expect(relativeAssets).toBe("assets");
+    });
libs/list-types/court-of-appeal-civil-daily-cause-list/src/config.test.ts (1)

20-24: Consider asserting the assets path exists.

This keeps the assets check on par with moduleRoot/pageRoutes by validating the directory on disk.

Proposed change
   it("should export assets as a valid directory path", () => {
     expect(assets).toBeDefined();
     expect(typeof assets).toBe("string");
     expect(assets).toContain("assets");
+    expect(existsSync(assets)).toBe(true);
   });
libs/list-types/rcj-standard-daily-cause-list/src/config.test.ts (1)

1-22: Align tests with standard config exports (assets).

Once assets is exported, include a basic path check so this test matches the standard config contract.

Proposed change
-import { moduleRoot, pageRoutes } from "./config.js";
+import { assets, moduleRoot, pageRoutes } from "./config.js";
   it("should have pageRoutes.path as a subdirectory of moduleRoot", () => {
     expect(pageRoutes.path.startsWith(moduleRoot)).toBe(true);
   });
+
+  it("should export assets as a valid directory path", () => {
+    expect(assets).toBeDefined();
+    expect(typeof assets).toBe("string");
+    expect(assets).toContain("assets");
+  });

Based on learnings, module config.ts must export standardised interfaces: pageRoutes, apiRoutes, prismaSchemas, assets.

libs/list-types/rcj-standard-daily-cause-list/src/models/types.ts (1)

1-11: Consider extracting StandardHearing to @hmcts/list-types-common.

This interface is duplicated verbatim in london-administrative-court-daily-cause-list and administrative-court-daily-cause-list. Centralising it would reduce maintenance overhead.

♻️ Proposed approach

Export from libs/list-types/common/src/models/types.ts:

export interface StandardHearing {
  venue: string;
  judge: string;
  time: string;
  caseNumber: string;
  caseDetails: string;
  hearingType: string;
  additionalInformation: string;
}

export type StandardHearingList = StandardHearing[];

Then import in each list-type module:

import { StandardHearing, StandardHearingList } from "@hmcts/list-types-common";
libs/list-types/administrative-court-daily-cause-list/src/config.test.ts (1)

20-24: Consider adding existsSync check for assets path.

The tests for moduleRoot and pageRoutes.path verify directory existence, but the assets test only checks the string contains "assets". For consistency, consider verifying the directory exists.

♻️ Suggested addition
 it("should export assets as a valid directory path", () => {
   expect(assets).toBeDefined();
   expect(typeof assets).toBe("string");
   expect(assets).toContain("assets");
+  expect(existsSync(assets)).toBe(true);
 });
libs/list-types/london-administrative-court-daily-cause-list/src/config.test.ts (1)

20-24: Same suggestion: add existsSync check for assets.

As with the administrative-court module, consider adding existence verification for consistency.

♻️ Suggested addition
 it("should export assets as a valid directory path", () => {
   expect(assets).toBeDefined();
   expect(typeof assets).toBe("string");
   expect(assets).toContain("assets");
+  expect(existsSync(assets)).toBe(true);
 });
libs/list-types/common/src/rendering/date-formatting.test.ts (1)

81-101: Good coverage for dd/MM/yyyy parsing.

The tests cover English and Welsh locales, different months, and single-digit day/month handling. Consider adding an edge case for invalid input (e.g., malformed date string) to ensure graceful handling.

libs/list-types/rcj-standard-daily-cause-list/src/pages/civil-courts-rcj-daily-cause-list.njk (3)

21-25: HTML concatenation in govukDetails.

The html parameter uses string concatenation which could be fragile if any of the text variables contain special characters. Ensure the source data is properly escaped before reaching this template.


27-33: Redundant aria-label on input.

The input has both a <label> element (albeit visually hidden) and an aria-label attribute. Having both is redundant; the <label> with for attribute already provides accessibility. Consider removing the aria-label to avoid potential screen reader confusion.

Suggested change
-      <input class="govuk-input govuk-!-width-one-half" id="case-search-input" name="search" type="text" aria-label="{{ common.searchCasesLabel }}">
+      <input class="govuk-input govuk-!-width-one-half" id="case-search-input" name="search" type="text">

48-60: Consider handling empty hearings state.

If the hearings array is empty, the table will render with headers but no body rows. Consider adding a message when there are no hearings to display, consistent with other templates that use noHearingsMessage.

Suggested change
         <tbody class="govuk-table__body">
+          {% if hearings.length == 0 %}
+            <tr class="govuk-table__row">
+              <td class="govuk-table__cell" colspan="7">{{ common.noHearingsMessage }}</td>
+            </tr>
+          {% endif %}
           {% for hearing in hearings %}
             <tr class="govuk-table__row">
libs/list-types/court-of-appeal-civil-daily-cause-list/src/pages/court-of-appeal-civil-daily-cause-list.njk (2)

29-33: Redundant aria-label on search input.

The <label for="case-search-input"> already provides accessibility for screen readers. The additional aria-label attribute is redundant and may cause the label to be announced twice.

Suggested fix
       <label class="govuk-label govuk-visually-hidden" for="case-search-input">
         {{ t.searchCasesLabel }}
       </label>
-      <input class="govuk-input govuk-!-width-one-half" id="case-search-input" name="search" type="text" aria-label="{{ t.searchCasesLabel }}">
+      <input class="govuk-input govuk-!-width-one-half" id="case-search-input" name="search" type="text">

35-71: Consider adding a visible heading for the daily hearings section.

The future judgments section has a visible <h2> heading (line 76), but the daily hearings section relies solely on aria-label on the table. A visible heading would improve consistency and navigation.

Suggested addition after line 37
     <div class="hearings-section" id="daily-hearings-section">
+      <h2 class="govuk-heading-m">{{ t.dailyHearingsTitle }}</h2>
       <div id="daily-hearings-table-container">
libs/list-types/rcj-standard-daily-cause-list/src/pages/senior-courts-costs-office-daily-cause-list.njk (1)

2-61: Use govukTable macro instead of raw table markup.

This aligns with the requirement to use GOV.UK component macros and keeps table semantics/spacing consistent. Based on learnings, please refactor the table to govukTable, ideally by building rows in the template or renderer.

libs/list-types/civil-and-family-daily-cause-list/src/pages/index.ts (1)

12-32: Validate artefactId format before querying.

The endpoint currently checks presence only; please validate the expected format (e.g., UUID) to satisfy the input‑validation requirement and fail fast with 400.

♻️ Suggested update (format validation)
 const __filename = fileURLToPath(import.meta.url);
 const __dirname = path.dirname(__filename);

 // Navigate to monorepo root (from libs/list-types/civil-and-family-daily-cause-list/src/pages/)
 const MONOREPO_ROOT = path.join(__dirname, "..", "..", "..", "..", "..");
 const TEMP_UPLOAD_DIR = path.join(MONOREPO_ROOT, "storage", "temp", "uploads");
+const ARTEFACT_ID_PATTERN =
+  /^[0-9a-fA-F-]{8}-[0-9a-fA-F-]{4}-[1-5][0-9a-fA-F-]{3}-[89abAB][0-9a-fA-F-]{3}-[0-9a-fA-F-]{12}$/;

 export const GET = async (req: Request, res: Response) => {
   const locale = res.locals.locale || "en";
   const t = locale === "cy" ? cy : en;

   const artefactId = req.query.artefactId as string;

-  if (!artefactId) {
+  if (!artefactId || !ARTEFACT_ID_PATTERN.test(artefactId)) {
     return res.status(400).render("errors/common", {
       en,
       cy,
       errorTitle: t.errorTitle,
       errorMessage: t.errorMessage
     });
   }

As per coding guidelines, input validation must be performed on all endpoints.

libs/list-types/care-standards-tribunal-weekly-hearing-list/src/pages/index.test.ts (2)

5-13: Rename the hoisted mock to SCREAMING_SNAKE_CASE.

This is a module‑level constant and should follow the constant naming convention; please rename it and update references.

♻️ Suggested rename
-const mockValidate = vi.hoisted(() => vi.fn());
+const MOCK_VALIDATE = vi.hoisted(() => vi.fn());

 vi.mock("@hmcts/list-types-common", () => ({
-  createJsonValidator: () => mockValidate
+  createJsonValidator: () => MOCK_VALIDATE
 }));

As per coding guidelines, constants should use SCREAMING_SNAKE_CASE.


244-292: Add a Welsh string assertion to cover translation wiring.

The Welsh‑locale test only checks locale: "cy" and doesn’t validate that Welsh strings (e.g., list title) are actually passed/rendered. Consider asserting the Welsh list title (sourced from the locale resource) in the render call or view model to catch regressions. Based on learnings, Welsh translations are required for all user‑facing text.

e2e-tests/tests/court-of-appeal-civil-viewing.spec.ts (1)

56-72: Prefer role/label selectors and deterministic waits to reduce flakiness.

The upload helpers rely on CSS selectors and waitForTimeout calls. Where possible, use getByRole/getByLabel/getByText and wait on visible elements or URL changes instead of fixed delays to make tests more resilient. As per coding guidelines, prefer getByRole/getByLabel/getByText before CSS selectors in E2E tests.

Also applies to: 206-221

libs/list-types/court-of-appeal-civil-daily-cause-list/src/schemas/court-of-appeal-civil-daily-cause-list.json (1)

1-86: Consider disallowing unknown fields in the schema.

Without additionalProperties: false, extra keys are silently accepted, which can mask payload issues. Consider adding it at the root and item levels.

♻️ Suggested hardening
 {
   "$schema": "http://json-schema.org/draft-07/schema#",
   "title": "Court of Appeal (Civil Division) Daily Cause List",
   "description": "Schema for Court of Appeal Civil with Daily hearings and Future judgments tabs",
   "type": "object",
+  "additionalProperties": false,
   "required": ["dailyHearings", "futureJudgments"],
   "properties": {
     "dailyHearings": {
       "type": "array",
       "items": {
         "type": "object",
+        "additionalProperties": false,
         "required": ["venue", "judge", "time", "caseNumber", "caseDetails", "hearingType"],
         "properties": {
           ...
         }
       }
     },
     "futureJudgments": {
       "type": "array",
       "items": {
         "type": "object",
+        "additionalProperties": false,
         "required": ["date", "venue", "judge", "time", "caseNumber", "caseDetails", "hearingType"],
         "properties": {
           ...
         }
       }
     }
   }
 }
libs/list-types/common/src/validation/json-validator.ts (1)

4-5: Consider improving type safety.

Multiple uses of any reduce type safety. The Ajv as any cast may be necessary due to ESM interop, but the compiledValidators map and error handling could be typed more strictly using AJV's built-in types.

♻️ Suggested improvement
+import type { ValidateFunction } from "ajv";
+
 const ajv = new (Ajv as any)({ allErrors: true });
-const compiledValidators = new Map<string, any>();
+const compiledValidators = new Map<string, ValidateFunction>();
libs/list-types/common/src/rendering/date-formatting.ts (1)

48-58: Consider defensive parsing for malformed date strings.

split("/") on an invalid input (e.g., empty string, missing parts) will produce NaN values, resulting in an Invalid Date. If upstream validation is guaranteed, this is acceptable; otherwise, consider early validation or a fallback.

🛠️ Optional defensive implementation
 export function formatDdMmYyyyDate(ddMMyyyyDate: string, locale: string): string {
   const [day, month, year] = ddMMyyyyDate.split("/");
+  if (!day || !month || !year) {
+    throw new Error(`Invalid date format: ${ddMMyyyyDate}`);
+  }
   const date = new Date(Number.parseInt(year, 10), Number.parseInt(month, 10) - 1, Number.parseInt(day, 10));
+  if (Number.isNaN(date.getTime())) {
+    throw new Error(`Invalid date: ${ddMMyyyyDate}`);
+  }
libs/list-types/rcj-standard-daily-cause-list/src/rendering/renderer.test.ts (1)

6-75: Use SCREAMING_SNAKE_CASE for the constant test data.

Rename mockHearings to align with the project constant naming rule.

Proposed refactor
-  const mockHearings: StandardHearingList = [
+  const MOCK_HEARINGS: StandardHearingList = [
@@
-    const result = renderStandardDailyCauseList(mockHearings, {
+    const result = renderStandardDailyCauseList(MOCK_HEARINGS, {
@@
-    const result = renderStandardDailyCauseList(mockHearings, {
+    const result = renderStandardDailyCauseList(MOCK_HEARINGS, {
@@
-    const result = renderStandardDailyCauseList(mockHearings, {
+    const result = renderStandardDailyCauseList(MOCK_HEARINGS, {
@@
-    const result = renderStandardDailyCauseList(mockHearings, {
+    const result = renderStandardDailyCauseList(MOCK_HEARINGS, {

As per coding guidelines, ...

libs/list-types/london-administrative-court-daily-cause-list/src/conversion/london-administrative-court-daily-cause-list-config.ts (1)

14-17: Avoid as any cast without justification.

The as any cast bypasses type checking. Consider defining a proper type for the converter function or adding a comment explaining why the cast is necessary.

💡 Suggested approach

If the type mismatch is due to createMultiSheetConverter returning Promise<Record<string, any[]>> while registerConverter expects a different signature, consider:

  1. Adding a type assertion with a more specific type
  2. Creating a wrapper type that matches the expected interface
  3. At minimum, add a // @ts-expect-error`` or comment explaining the type incompatibility
libs/list-types/london-administrative-court-daily-cause-list/src/pages/london-administrative-court-daily-cause-list.njk (1)

28-34: Minor accessibility redundancy.

The input has both a visually-hidden <label> (line 30-32) and an aria-label attribute (line 33). While not harmful, the aria-label is redundant when a proper <label for="..."> association exists.

🔧 Suggested fix
       <label class="govuk-label govuk-visually-hidden" for="case-search-input">
         {{ t.searchCasesLabel }}
       </label>
-      <input class="govuk-input govuk-!-width-one-half" id="case-search-input" name="search" type="text" aria-label="{{ t.searchCasesLabel }}">
+      <input class="govuk-input govuk-!-width-one-half" id="case-search-input" name="search" type="text">
libs/list-types/care-standards-tribunal-weekly-hearing-list/src/pages/index.ts (1)

54-55: Consider removing file path from error log.

Logging the full file path (jsonFilePath) may expose internal directory structure. Log a sanitised reference instead.

-      console.error(`Error reading JSON file at ${jsonFilePath}:`, error);
+      console.error(`Error reading JSON file for artefact ${artefactId}:`, error);
libs/list-types/care-standards-tribunal-weekly-hearing-list/src/pages/care-standards-tribunal-weekly-hearing-list.njk (1)

44-47: Redundant labelling on search input.

The input has both a <label> element (line 44-46) and an aria-label attribute (line 47). The visually hidden label is sufficient; the aria-label is redundant and may cause screen readers to announce different text if values diverge.

-      <input class="govuk-input govuk-!-width-one-half" id="case-search-input" name="search" type="text" aria-label="{{ t.searchCasesLabel }}">
+      <input class="govuk-input govuk-!-width-one-half" id="case-search-input" name="search" type="text">
libs/list-types/court-of-appeal-civil-daily-cause-list/src/rendering/renderer.ts (2)

67-67: Consider extracting hardcoded Welsh title to translation files.

The inline Welsh translation differs from other list-types that use separate cy.ts/en.ts locale files. Centralising translations improves maintainability.

-      listTitle: options.locale === "cy" ? "Rhestr Achosion Dyddiol y Llys Apêl (Adran Sifil)" : "Court of Appeal (Civil Division) Daily Cause List",
+      listTitle: options.listTitle,

Then pass listTitle via RenderOptions from the page controller, sourced from the locale file.


4-34: Module ordering: consider moving interfaces to the bottom.

Per coding guidelines, interfaces and types should be at the bottom of the module. Currently they precede the functions.

libs/list-types/administrative-court-daily-cause-list/src/pages/index.test.ts (1)

57-129: Prefer typed fixtures over as any.

as any hides type regressions; consider importing the artefact type and using satisfies or a typed fixture helper instead.

libs/list-types/administrative-court-daily-cause-list/src/rendering/renderer.test.ts (1)

134-157: Test name and data don’t align.

This case says “undefined” but uses an empty string and duplicates the earlier empty-info test. Consider removing it or making it truly undefined.

libs/list-types/court-of-appeal-civil-daily-cause-list/src/pages/index.ts (1)

64-75: Consider distinguishing between file-not-found and other I/O errors.

Currently, all readFile errors return 404. A permissions error or disk failure should likely return 500 instead, to aid debugging.

libs/list-types/administrative-court-daily-cause-list/src/pages/administrative-court-daily-cause-list.njk (1)

29-35: Redundant labelling on search input.

The input has both a <label> (visually hidden) and an aria-label. The aria-label overrides the label association for screen readers. Consider removing one to avoid duplication.

♻️ Proposed fix
     <div class="govuk-form-group search-container">
       <h2 class="govuk-heading-s">{{ common.searchCasesTitle }}</h2>
       <label class="govuk-label govuk-visually-hidden" for="case-search-input">
         {{ common.searchCasesLabel }}
       </label>
-      <input class="govuk-input govuk-!-width-one-half" id="case-search-input" name="search" type="text" aria-label="{{ common.searchCasesLabel }}">
+      <input class="govuk-input govuk-!-width-one-half" id="case-search-input" name="search" type="text">
     </div>
libs/list-types/rcj-standard-daily-cause-list/src/pages/index.test.ts (1)

131-173: Consider using it.each for parameterised list type tests.

The loop-based approach works but it.each provides better test naming and failure isolation.

♻️ Suggested refactor
it.each([10, 11, 12, 13, 14, 15, 16, 17])(
  "should render the list successfully for listTypeId %i",
  async (listTypeId) => {
    // test body
  }
);
libs/list-types/court-of-appeal-civil-daily-cause-list/src/conversion/court-of-appeal-civil-daily-cause-list-config.ts (1)

70-81: Remove the as any cast by aligning converter typings.

The cast erodes type safety and defeats strict TS. Please adjust the converter typing or add a typed adapter that matches registerConverter’s expected signature. As per coding guidelines, avoid any without justification.

libs/list-types/rcj-standard-daily-cause-list/src/pages/index.ts (1)

130-131: Remove any when accessing list content.

Please type the translation map (e.g., Record<number, ListContent>) so listTypeId indexing is type‑safe without as any. As per coding guidelines, avoid any without justification.

libs/list-types/rcj-standard-daily-cause-list/src/rendering/renderer.ts (1)

4-31: Reorder declarations to match module ordering guidance.

Interfaces/types should be at the bottom of the module, after exported functions. Please move RenderOptions, RenderedHearing, and RenderedData beneath renderStandardDailyCauseList. As per coding guidelines, keep interfaces and types at the bottom.

libs/list-types/common/src/conversion/multi-sheet-converter.ts (2)

1-26: Replace any with ExcelJS types for stricter typing.

any weakens safety and conflicts with strict TypeScript guidance. Please use ExcelJS types and unknown[] (or a concrete row type) for the return/aggregate types. As per coding guidelines, avoid any without justification.

♻️ Suggested typing update
-import ExcelJSPkg from "exceljs";
+import ExcelJSPkg, { type Worksheet, type Row, type Cell } from "exceljs";
 import { convertExcelToJson, type ExcelConverterConfig } from "./excel-to-json.js";
 
 export async function convertSheetToJson(worksheet: any, config: ExcelConverterConfig): Promise<any[]> {
+export async function convertSheetToJson(worksheet: Worksheet, config: ExcelConverterConfig): Promise<unknown[]> {
   // Create a temporary buffer from the sheet
   const workbook = new Workbook();
   const tempSheet = workbook.addWorksheet("temp");
 
   // Copy all rows from source to temp worksheet
-  worksheet.eachRow((row: any, rowNumber: number) => {
+  worksheet.eachRow((row: Row, rowNumber: number) => {
     const newRow = tempSheet.getRow(rowNumber);
-    row.eachCell((cell: any, colNumber: number) => {
+    row.eachCell((cell: Cell, colNumber: number) => {
       newRow.getCell(colNumber).value = cell.value;
     });
     newRow.commit();
   });
 
-export async function createMultiSheetConverter(buffer: Buffer, sheets: SheetConfig[]): Promise<Record<string, any[]>> {
+export async function createMultiSheetConverter(buffer: Buffer, sheets: SheetConfig[]): Promise<Record<string, unknown[]>> {
   const workbook = new Workbook();
   // `@ts-expect-error` - ExcelJS types expect Node Buffer but accepts our Buffer type at runtime
   await workbook.xlsx.load(buffer);
@@
-  const result: Record<string, any[]> = {};
+  const result: Record<string, unknown[]> = {};

Also applies to: 54-65


28-37: Move SheetConfig below exported functions.

Keep interfaces/types at the bottom of the module for consistent ordering. As per coding guidelines, place interfaces and types after exported functions.

libs/list-types/administrative-court-daily-cause-list/src/pages/index.ts (2)

36-36: Add a POST handler to match page-controller conventions

Page controllers in this repo are expected to export both GET and POST handlers; consider adding a POST that returns 405 or delegates to GET as appropriate. Based on learnings, this aligns with established page-controller patterns.


122-124: Avoid any when indexing locale content

The any cast bypasses strict typing. Consider typing the locale dictionary and indexing via a typed key instead. As per coding guidelines, avoid any without justification.

♻️ Suggested change
-    const listContent = (t as any)[listTypeId] || {};
+    type ListContent = typeof en[20];
+    const listContent = (t as Record<number, ListContent>)[listTypeId] ?? {};

Comment on lines +74 to +175
test.describe("Administrative Court Daily Cause Lists - Viewing @nightly", () => {
test.beforeEach(async ({ page }) => {
await authenticateSystemAdmin(page);
});

test("should view Birmingham Administrative Court list with English and Welsh content", async ({ page }) => {
// Upload list
await uploadAdminCourtList(page, "20");

// Navigate to summary of publications
await page.goto("/summary-of-publications?locationId=9001");
await page.waitForTimeout(1000);

// Find and click the publication link
const publicationLinks = page.locator('.govuk-list a[href*="artefactId="]');
await expect(publicationLinks.first()).toBeVisible();
const firstLinkHref = await publicationLinks.first().getAttribute("href");
expect(firstLinkHref).toContain("/birmingham-administrative-court-daily-cause-list?artefactId=");

await publicationLinks.first().click();
await page.waitForLoadState("networkidle");

// Verify English content
await expect(page.locator("h1")).toContainText("Birmingham Administrative Court Daily Cause List");
await expect(page.locator("body")).toContainText("List for 15 January 2026");
await expect(page.locator("body")).toContainText("Last updated");
await expect(page.locator("body")).toContainText("Court 1");
await expect(page.locator("body")).toContainText("Mr Justice Williams");
await expect(page.locator("body")).toContainText("R (Smith) v Secretary of State");

// Verify time normalization (dot replaced with colon)
await expect(page.locator("tbody")).toContainText("10:00am");
await expect(page.locator("tbody")).toContainText("2:30pm");

// Test Welsh translation
await page.getByRole("link", { name: "Cymraeg" }).click();
await page.waitForLoadState("networkidle");
await expect(page.locator("body")).toContainText("Rhestr ar gyfer 15 Ionawr 2026");
await expect(page.locator("body")).toContainText("Diweddarwyd ddiwethaf");
await expect(page.locator("body")).toContainText("Lleoliad");

// Test accessibility
const accessibilityScanResults = await new AxeBuilder({ page })
.disableRules(["target-size", "link-name"])
.analyze();
expect(accessibilityScanResults.violations).toEqual([]);

// Test table search functionality
const searchInput = page.locator('input[id="case-search-input"]');
await expect(searchInput).toBeVisible();
await searchInput.fill("Smith");
await page.waitForTimeout(500);
await expect(page.locator("tbody tr:visible")).toHaveCount(1);
});

test("should view Leeds Administrative Court list with proper formatting", async ({ page }) => {
await uploadAdminCourtList(page, "21");

await page.goto("/summary-of-publications?locationId=9001");
await page.waitForTimeout(1000);

const publicationLinks = page.locator('.govuk-list a[href*="artefactId="]');
await publicationLinks.first().click();
await page.waitForLoadState("networkidle");

// Verify page loads correctly
await expect(page.locator("h1")).toContainText("Leeds Administrative Court Daily Cause List");
await expect(page.locator("body")).toContainText("List for");

// Verify table headers
await expect(page.locator("th")).toContainText("Venue");
await expect(page.locator("th")).toContainText("Judge");
await expect(page.locator("th")).toContainText("Time");
await expect(page.locator("th")).toContainText("Case Number");

// Test accessibility
const accessibilityScanResults = await new AxeBuilder({ page })
.disableRules(["target-size", "link-name"])
.analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});

test("should view Manchester Administrative Court list and verify data source", async ({ page }) => {
await uploadAdminCourtList(page, "23");

await page.goto("/summary-of-publications?locationId=9001");
await page.waitForTimeout(1000);

const publicationLinks = page.locator('.govuk-list a[href*="artefactId="]');
await publicationLinks.first().click();
await page.waitForLoadState("networkidle");

// Verify data source is shown
await expect(page.locator("body")).toContainText("Data source");
await expect(page.locator("body")).toContainText("Manual Upload");

// Test Welsh data source
await page.getByRole("link", { name: "Cymraeg" }).click();
await page.waitForLoadState("networkidle");
await expect(page.locator("body")).toContainText("Ffynhonnell data");
await expect(page.locator("body")).toContainText("Llwytho â Llaw");
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Single‑journey E2E requirements are not met.

These tests don’t include validation checks or keyboard navigation within the same journey as Welsh and accessibility assertions. Please consolidate into one test flow (or add a dedicated single journey that covers all four). Based on learnings, include validation checks, Welsh translation checks, accessibility checks, and keyboard navigation within a single E2E test journey.

Comment on lines +93 to +163
test("should view Court of Appeal Civil list with daily hearings and future judgments sections", async ({ page }) => {
// Upload list
await uploadCourtOfAppealCivilList(page);

// Navigate to summary of publications
await page.goto("/summary-of-publications?locationId=9001");
await page.waitForTimeout(1000);

// Find and click the publication link
const publicationLinks = page.locator('.govuk-list a[href*="artefactId="]');
await expect(publicationLinks.first()).toBeVisible();
const firstLinkHref = await publicationLinks.first().getAttribute("href");
expect(firstLinkHref).toContain("/court-of-appeal-civil-division-daily-cause-list?artefactId=");

await publicationLinks.first().click();
await page.waitForLoadState("networkidle");

// Verify English content
await expect(page.locator("h1")).toContainText("Court of Appeal (Civil Division) Daily Cause List");
await expect(page.locator("body")).toContainText("List for 15 January 2026");
await expect(page.locator("body")).toContainText("Last updated");

// Verify daily hearings section
await expect(page.locator("body")).toContainText("Lord Justice Smith");
await expect(page.locator("body")).toContainText("Appellant v Respondent");
await expect(page.locator("body")).toContainText("CA-2025-000123");

// Verify future judgments section
await expect(page.locator("h2")).toContainText("Future Judgments");
await expect(page.locator("body")).toContainText("Lord Justice Williams");
await expect(page.locator("body")).toContainText("Estate of Smith v Executor");
await expect(page.locator("body")).toContainText("20 January 2026");

// Verify future judgments table has Date column
const futureJudgmentsTable = page.locator("#future-judgments-table-container table");
await expect(futureJudgmentsTable.locator("th").first()).toContainText("Date");

// Verify time normalization (dot to colon)
await expect(page.locator("tbody")).toContainText("10:30am");
await expect(page.locator("tbody")).toContainText("2:00pm");

// Test Welsh translation
await page.getByRole("link", { name: "Cymraeg" }).click();
await page.waitForLoadState("networkidle");
await expect(page.locator("h1")).toContainText("Rhestr Achosion Dyddiol y Llys Apêl (Adran Sifil)");
await expect(page.locator("body")).toContainText("Rhestr ar gyfer 15 Ionawr 2026");
await expect(page.locator("body")).toContainText("Diweddarwyd ddiwethaf");

// Future Judgments section in Welsh
await expect(page.locator("h2")).toContainText("Dyfarniadau yn y Dyfodol");

// Test accessibility
const accessibilityScanResults = await new AxeBuilder({ page })
.disableRules(["target-size", "link-name"])
.analyze();
expect(accessibilityScanResults.violations).toEqual([]);

// Test table search across both sections
const searchInput = page.locator('input[id="case-search-input"]');
await expect(searchInput).toBeVisible();

// Switch back to English for search test
await page.getByRole("link", { name: "English" }).click();
await page.waitForLoadState("networkidle");

await searchInput.fill("Estate");
await page.waitForTimeout(500);
// Should show only the future judgment row
await expect(page.locator("tbody tr:visible")).toHaveCount(1);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Single test journey is missing required validation + keyboard checks.

Welsh and accessibility checks are in the main test, but keyboard navigation sits in a separate test and there’s no validation step. Please fold the keyboard/back‑to‑top assertions into the main viewing journey and add a small validation check (e.g., submit without a file and assert the error summary) within the same flow. Based on learnings, E2E journeys must include validation, Welsh translation, accessibility, and keyboard navigation checks in one test.

Also applies to: 249-274

Comment on lines +87 to +198
test.describe("London Administrative Court Daily Cause List - Viewing @nightly", () => {
test.beforeEach(async ({ page }) => {
await authenticateSystemAdmin(page);
});

test("should view London Administrative Court list with main hearings and planning court sections", async ({ page }) => {
// Upload list
await uploadLondonAdminCourtList(page);

// Navigate to summary of publications
await page.goto("/summary-of-publications?locationId=9001");
await page.waitForTimeout(1000);

// Find and click the publication link
const publicationLinks = page.locator('.govuk-list a[href*="artefactId="]');
await expect(publicationLinks.first()).toBeVisible();
const firstLinkHref = await publicationLinks.first().getAttribute("href");
expect(firstLinkHref).toContain("/london-administrative-court-daily-cause-list?artefactId=");

await publicationLinks.first().click();
await page.waitForLoadState("networkidle");

// Verify English content
await expect(page.locator("h1")).toContainText("London Administrative Court Daily Cause List");
await expect(page.locator("body")).toContainText("List for 15 January 2026");
await expect(page.locator("body")).toContainText("Last updated");

// Verify main hearings section
await expect(page.locator("body")).toContainText("R (Brown) v Home Secretary");
await expect(page.locator("body")).toContainText("Mr Justice Roberts");

// Verify planning court section
await expect(page.locator("h2")).toContainText("Planning Court");
await expect(page.locator("body")).toContainText("R (Developer Ltd) v Council");
await expect(page.locator("body")).toContainText("Mr Justice Black");

// Verify time normalization
await expect(page.locator("tbody")).toContainText("10:00am");
await expect(page.locator("tbody")).toContainText("2:30pm");

// Test Welsh translation
await page.getByRole("link", { name: "Cymraeg" }).click();
await page.waitForLoadState("networkidle");
await expect(page.locator("h1")).toContainText("Rhestr Achosion Dyddiol Llys Gweinyddol Llundain");
await expect(page.locator("body")).toContainText("Rhestr ar gyfer 15 Ionawr 2026");
await expect(page.locator("body")).toContainText("Diweddarwyd ddiwethaf");
await expect(page.locator("h2")).toContainText("Llys Cynllunio");

// Test accessibility
const accessibilityScanResults = await new AxeBuilder({ page })
.disableRules(["target-size", "link-name"])
.analyze();
expect(accessibilityScanResults.violations).toEqual([]);

// Test table search across both sections
const searchInput = page.locator('input[id="case-search-input"]');
await expect(searchInput).toBeVisible();

// Switch back to English for search test
await page.getByRole("link", { name: "English" }).click();
await page.waitForLoadState("networkidle");

await searchInput.fill("Developer");
await page.waitForTimeout(500);
// Should show only the planning court row
await expect(page.locator("tbody tr:visible")).toHaveCount(1);
});

test("should display important information section correctly", async ({ page }) => {
await uploadLondonAdminCourtList(page);

await page.goto("/summary-of-publications?locationId=9001");
await page.waitForTimeout(1000);

const publicationLinks = page.locator('.govuk-list a[href*="artefactId="]');
await publicationLinks.first().click();
await page.waitForLoadState("networkidle");

// Check for important information section
const importantInfoDetails = page.locator(".govuk-details");
await expect(importantInfoDetails).toBeVisible();
await expect(importantInfoDetails.locator(".govuk-details__summary-text")).toContainText("Important information");

// Expand the details section
await importantInfoDetails.locator(".govuk-details__summary").click();
await expect(importantInfoDetails).toContainText("Hearings take place in public");

// Check for judgments section
await expect(importantInfoDetails).toContainText("Judgments");
});

test("should verify keyboard navigation works correctly", async ({ page }) => {
await uploadLondonAdminCourtList(page);

await page.goto("/summary-of-publications?locationId=9001");
await page.waitForTimeout(1000);

const publicationLinks = page.locator('.govuk-list a[href*="artefactId="]');
await publicationLinks.first().click();
await page.waitForLoadState("networkidle");

// Test keyboard navigation to search input
await page.keyboard.press("Tab");
await page.keyboard.press("Tab");
const searchInput = page.locator('input[id="case-search-input"]');
await expect(searchInput).toBeFocused();

// Test keyboard navigation to table
await page.keyboard.press("Tab");
const focusedElement = page.locator(":focus");
await expect(focusedElement).toBeVisible();
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Single-journey E2E requirements are not met.

The suite splits Welsh/accessibility and keyboard navigation across tests and does not include any validation checks. Please consolidate these into one end‑to‑end journey (or add a single test that includes all four elements). Based on learnings, include validation checks, Welsh translation checks, accessibility checks, and keyboard navigation within a single E2E test journey.

Comment on lines +79 to +127
test("should view Civil Courts at RCJ list with English and Welsh content", async ({ page }) => {
// Upload list
await uploadRCJList(page, "10");

// Navigate to summary of publications
await page.goto("/summary-of-publications?locationId=9001");
await page.waitForTimeout(1000);

// Find and click the publication link
const publicationLinks = page.locator('.govuk-list a[href*="artefactId="]');
await expect(publicationLinks.first()).toBeVisible();
const firstLinkHref = await publicationLinks.first().getAttribute("href");
expect(firstLinkHref).toContain("/civil-courts-rcj-daily-cause-list?artefactId=");

await publicationLinks.first().click();
await page.waitForLoadState("networkidle");

// Verify English content
await expect(page.locator("h1")).toContainText("Civil Courts at the Royal Courts of Justice Daily Cause List");
await expect(page.locator("body")).toContainText("List for 15 January 2026");
await expect(page.locator("body")).toContainText("Last updated");
await expect(page.locator("body")).toContainText("Court 1");
await expect(page.locator("body")).toContainText("Mr Justice Smith");
await expect(page.locator("body")).toContainText("R v Jones");

// Test Welsh translation
await page.getByRole("link", { name: "Cymraeg" }).click();
await page.waitForLoadState("networkidle");
await expect(page.locator("body")).toContainText("Rhestr ar gyfer 15 Ionawr 2026");
await expect(page.locator("body")).toContainText("Diweddarwyd ddiwethaf");
await expect(page.locator("body")).toContainText("Lleoliad");

// Test accessibility
const accessibilityScanResults = await new AxeBuilder({ page })
.disableRules(["target-size", "link-name"])
.analyze();
expect(accessibilityScanResults.violations).toEqual([]);

// Test table search functionality
const searchInput = page.locator('input[id="case-search-input"]');
await expect(searchInput).toBeVisible();
await searchInput.fill("Jones");
await page.waitForTimeout(500);
await expect(page.locator("tbody tr:visible")).toHaveCount(1);

// Test back to top link
const backToTopLink = page.getByRole("link", { name: /Yn ôl i frig y dudalen/i });
await expect(backToTopLink).toBeVisible();
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add validation + keyboard navigation checks in the same journey.

This flow includes Welsh and accessibility checks but does not assert form validation or keyboard navigation within the same journey. Please add a lightweight validation check (e.g., submit with a required field missing) and a Tab/focus-order assertion in this test. Based on learnings, include validation and keyboard navigation in the same E2E journey.

Comment on lines +85 to +98
const jsonFilePath = path.join(TEMP_UPLOAD_DIR, `${artefactId}.json`);

let jsonContent: string;
try {
jsonContent = await readFile(jsonFilePath, "utf-8");
} catch (error) {
console.error(`Error reading JSON file at ${jsonFilePath}:`, error);
return res.status(404).render("errors/common", {
en,
cy,
errorTitle: "Not Found",
errorMessage: "The requested list could not be found"
});
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Prevent path traversal when building the JSON file path.

artefactId is interpolated into a filesystem path without sanitisation. A crafted value can escape TEMP_UPLOAD_DIR. Please validate the ID and/or enforce the resolved path is under the intended directory.

🔒 Suggested hardening
 const TEMP_UPLOAD_DIR = path.join(MONOREPO_ROOT, "storage", "temp", "uploads");
+const ARTEFACT_ID_PATTERN = /^[0-9a-f-]+$/i;
 const schemaPath = path.join(__dirname, "../schemas/rcj-standard-daily-cause-list.json");
 const validate = createJsonValidator(schemaPath);
@@
   if (!artefactId) {
     return res.status(400).render("errors/common", {
@@
     });
   }
+
+  if (!ARTEFACT_ID_PATTERN.test(artefactId)) {
+    return res.status(400).render("errors/common", {
+      en,
+      cy,
+      errorTitle: "Bad Request",
+      errorMessage: "Invalid artefactId parameter"
+    });
+  }
@@
-    const jsonFilePath = path.join(TEMP_UPLOAD_DIR, `${artefactId}.json`);
+    const jsonFilePath = path.join(TEMP_UPLOAD_DIR, `${artefactId}.json`);
+    const resolvedJsonFilePath = path.resolve(jsonFilePath);
+    if (!resolvedJsonFilePath.startsWith(`${TEMP_UPLOAD_DIR}${path.sep}`)) {
+      return res.status(400).render("errors/common", {
+        en,
+        cy,
+        errorTitle: "Bad Request",
+        errorMessage: "Invalid artefactId parameter"
+      });
+    }

Comment on lines +102 to +111
const validationResult = validate(jsonData);
if (!validationResult.isValid) {
console.error("Validation errors:", validationResult.errors);
return res.status(400).render("errors/common", {
en,
cy,
errorTitle: "Invalid Data",
errorMessage: "The list data is invalid"
});
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid logging potentially sensitive validation details.

validationResult.errors may include user data; logging it risks PII exposure. Log only a summary/count instead. As per coding guidelines, avoid logging sensitive data.

🧹 Safer logging
-    if (!validationResult.isValid) {
-      console.error("Validation errors:", validationResult.errors);
+    if (!validationResult.isValid) {
+      console.error("Validation failed for RCJ Standard Daily Cause List", {
+        errorCount: validationResult.errors?.length ?? 0
+      });

@@ -0,0 +1,72 @@
{% extends "layouts/base-template.njk" %}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use the required base template name.

Line 1 extends layouts/base-template.njk, but the project requirement for templates under libs/*/src/pages/**/*.njk is layouts/base-templates.njk. If the singular template doesn’t exist, rendering will fail; even if it does, this breaks consistency.

💡 Suggested change
-{% extends "layouts/base-template.njk" %}
+{% extends "layouts/base-templates.njk" %}

As per learnings, templates must extend layouts/base-templates.njk.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{% extends "layouts/base-template.njk" %}
{% extends "layouts/base-templates.njk" %}

@@ -0,0 +1,72 @@
{% extends "layouts/base-template.njk" %}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Align base layout with required layouts/base-templates.njk.

This file extends layouts/base-template.njk, but templates in this path are required to extend layouts/base-templates.njk. That mismatch can break layout rendering or standard components.

💡 Proposed fix
-{% extends "layouts/base-template.njk" %}
+{% extends "layouts/base-templates.njk" %}

Based on learnings, this template must extend layouts/base-templates.njk.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{% extends "layouts/base-template.njk" %}
{% extends "layouts/base-templates.njk" %}

@@ -0,0 +1,72 @@
{% extends "layouts/base-template.njk" %}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Base template name does not match required layout.

The template should extend layouts/base-templates.njk per project guidance; the current name is different and may break rendering. Based on learnings, please align the base template reference.

Suggested fix
-{% extends "layouts/base-template.njk" %}
+{% extends "layouts/base-templates.njk" %}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{% extends "layouts/base-template.njk" %}
{% extends "layouts/base-templates.njk" %}

@github-actions

github-actions Bot commented Jan 16, 2026

Copy link
Copy Markdown
Contributor

🎭 Playwright E2E Test Results

207 tests   207 ✅  20m 9s ⏱️
 29 suites    0 💤
  1 files      0 ❌

Results for commit 1c30ae5.

♻️ This comment has been updated with latest results.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
e2e-tests/tests/care-standards-tribunal-upload.spec.ts (2)

265-278: Bug: Hardcoded artefactId renders test ineffective.

Line 267 calls completeCSTUploadFlow but discards the returned artefactId. Line 272 then navigates to ?artefactId=test, which won't find the actual published list.

🐛 Proposed fix
     test("should display the published CST list with correct formatting", async ({ page }) => {
       // Upload and publish the list
-      await completeCSTUploadFlow(page);
+      const artefactId = await completeCSTUploadFlow(page);
 
       // Navigate to the published list
-      // Note: In a real scenario, you'd navigate via search or direct URL
-      // For now, we'll construct the URL pattern
-      await page.goto("/care-standards-tribunal-weekly-hearing-list?artefactId=test");
-      await page.waitForTimeout(1000);
+      await navigateToPublishedList(page, artefactId);
 
       // Verify page loads (may need adjustment based on actual implementation)
       const heading = page.locator("h1");
       await expect(heading).toBeVisible();
     });

545-550: Add explanatory comment for disabled accessibility rules.

Disabling target-size and link-name rules is a widespread pattern across multiple E2E tests. Other test files document this reasoning with comments (e.g. "Known GOV.UK Design System footer issues"). Add a similar comment to this file to explain why these specific rules are disabled, so the decision is transparent and maintainable.

🧹 Nitpick comments (6)
e2e-tests/tests/care-standards-tribunal-upload.spec.ts (6)

165-197: Avoid waitForTimeout - prefer explicit waits.

waitForTimeout(1000) on line 167 is flaky. Use waitForLoadState('networkidle') or wait for a specific element that indicates page readiness.

♻️ Suggested improvement
 async function uploadCSTExcel(page: Page, excelBuffer: Buffer, expectSuccess = true) {
   await page.goto("/non-strategic-upload?locationId=9001");
-  await page.waitForTimeout(1000);
+  await page.waitForLoadState("networkidle");
 
   // Select Care Standards Tribunal (listTypeId === 9)

199-227: Replace waitForTimeout calls with explicit waits.

Lines 209 and 226 use arbitrary timeouts. This pattern can cause flaky tests across different environments.

♻️ Suggested improvement
   // Navigate to summary of publications to find the artefactId
   await page.goto("/summary-of-publications");
-  await page.waitForTimeout(1000);
+  await page.waitForLoadState("networkidle");
 
   // Find the first CST publication link
   const publicationLinks = page.locator('.govuk-list a[href*="care-standards-tribunal-weekly-hearing-list?artefactId="]');
 async function navigateToPublishedList(page: Page, artefactId: string) {
   await page.goto(`/care-standards-tribunal-weekly-hearing-list?artefactId=${artefactId}`);
-  await page.waitForTimeout(1000);
+  await page.waitForLoadState("networkidle");
 }

356-371: Consider using getByText for cleaner assertions.

The loop iterating through paragraphs to find "Last updated" text is verbose. Playwright's getByText with a regex would be more idiomatic per coding guidelines.

♻️ Suggested improvement
-      // Verify "Last updated" date and time line
-      const bodyParagraphs = page.locator(".govuk-body");
-      let foundLastUpdated = false;
-      for (let i = 0; i < await bodyParagraphs.count(); i++) {
-        const text = await bodyParagraphs.nth(i).textContent();
-        if (text?.includes("Last updated")) {
-          foundLastUpdated = true;
-          expect(text).toMatch(/Last updated \d{1,2} \w+ \d{4} at \d{1,2}(:\d{2})?(am|pm)/);
-          break;
-        }
-      }
-      expect(foundLastUpdated).toBeTruthy();
+      // Verify "Last updated" date and time line
+      const lastUpdated = page.getByText(/Last updated \d{1,2} \w+ \d{4} at \d{1,2}(:\d{2})?(am|pm)/);
+      await expect(lastUpdated).toBeVisible();

491-504: Prefer getByLabel or getByRole for the search input.

Per coding guidelines, selector priority should be: getByRole(), getByLabel(), getByText(), getByTestId(). Using #case-search-input is less accessible-friendly.

♻️ Suggested improvement
-      const searchInput = page.locator("#case-search-input");
+      const searchInput = page.getByRole("textbox", { name: /search/i });
       await expect(searchInput).toBeVisible();

If the input lacks an accessible name, consider adding a label to the component.


578-598: Keyboard navigation test could be more thorough.

The test only tabs through 5 elements. Consider verifying that key interactive elements (form inputs, buttons, links) are reachable and that focus indicators are visible.


237-239: Prefer waitForLoadState over waitForTimeout.

Line 238 uses waitForTimeout(1000). This pattern appears throughout the file and should be replaced with deterministic waits.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0a50927 and 1bc20b1.

📒 Files selected for processing (1)
  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use camelCase for TypeScript variables: userId, caseDetails, documentId
Use PascalCase for classes and interfaces: UserService, CaseRepository. NO I prefix.
Use kebab-case for file and directory names: user-service.ts, case-management/
Use boolean variable names with is/has/can prefix: isActive, hasAccess, canEdit
Module ordering: constants at the top, exported functions next, other functions in order of use, interfaces and types at the bottom
Always add .js extension to relative imports in ES modules (e.g., import { foo } from "./bar.js")
Use workspace aliases for imports (@hmcts/*) instead of relative paths across packages
Use strict TypeScript mode enabled; no any without justification
Use parameterized database queries with Prisma (no raw SQL string concatenation)
Do not include sensitive data in logs

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

Use SCREAMING_SNAKE_CASE for constants: MAX_FILE_SIZE, DEFAULT_TIMEOUT

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
**/*.{test,spec}.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Test files must be co-located with source code using *.test.ts or *.spec.ts naming pattern

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
e2e-tests/**/*.spec.ts

📄 CodeRabbit inference engine (CLAUDE.md)

e2e-tests/**/*.spec.ts: E2E tests must be located in e2e-tests/ directory with *.spec.ts naming pattern
Tag nightly-only E2E tests with @nightly in the test title
E2E tests should use selectors in priority order: getByRole(), getByLabel(), getByText(), getByTestId()
Accessibility testing (WCAG 2.2 AA) is mandatory - include axe-core checks in E2E tests
Include validation checks, Welsh translation checks, accessibility checks, and keyboard navigation within a single E2E test journey
Do not test visual styling (fonts, colors, margins, padding) in E2E tests

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Input validation must be performed on all endpoints

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
**/*.{ts,tsx,js,mjs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not use CommonJS - ES modules only with import/export syntax

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
🧠 Learnings (10)
📓 Common learnings
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Welsh translations are required for all user-facing text
Learnt from: junaidiqbalmoj
Repo: hmcts/cath-service PR: 106
File: libs/system-admin-pages/src/pages/reference-data-upload/index.test.ts:84-160
Timestamp: 2025-11-20T09:59:16.776Z
Learning: In the cath-service repository, Welsh localization (lng=cy) is not required for admin screens (system-admin-pages), so locale preservation in admin screen redirects is not necessary.
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : Include validation checks, Welsh translation checks, accessibility checks, and keyboard navigation within a single E2E test journey

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : Accessibility testing (WCAG 2.2 AA) is mandatory - include axe-core checks in E2E tests

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : E2E tests must be located in `e2e-tests/` directory with `*.spec.ts` naming pattern

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : Tag nightly-only E2E tests with `nightly` in the test title

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : Do not test visual styling (fonts, colors, margins, padding) in E2E tests

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : E2E tests should use selectors in priority order: getByRole(), getByLabel(), getByText(), getByTestId()

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to libs/*/src/pages/**/*.ts : Provide both `en` and `cy` language objects in page controllers for English and Welsh support

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Welsh translations are required for all user-facing text

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-11-20T09:59:16.776Z
Learnt from: junaidiqbalmoj
Repo: hmcts/cath-service PR: 106
File: libs/system-admin-pages/src/pages/reference-data-upload/index.test.ts:84-160
Timestamp: 2025-11-20T09:59:16.776Z
Learning: In the cath-service repository, Welsh localization (lng=cy) is not required for admin screens (system-admin-pages), so locale preservation in admin screen redirects is not necessary.

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: SonarQube Analysis
  • GitHub Check: E2E Tests
🔇 Additional comments (1)
e2e-tests/tests/care-standards-tribunal-upload.spec.ts (1)

600-653: Comprehensive Welsh translation coverage - LGTM.

The test verifies Welsh translations for page title, "week commencing", "last updated", "Important information", all table headers, data source, "Back to top", and search label. Based on learnings, Welsh translations are required for all user-facing text.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
e2e-tests/tests/care-standards-tribunal-upload.spec.ts (1)

262-275: Bug: Test ignores returned artefactId and uses hardcoded value.

completeCSTUploadFlow returns the actual artefactId, but line 269 navigates to ?artefactId=test. This test will not validate the published list content correctly.

Proposed fix
     test("should display the published CST list with correct formatting", async ({ page }) => {
       // Upload and publish the list
-      await completeCSTUploadFlow(page);
+      const artefactId = await completeCSTUploadFlow(page);

       // Navigate to the published list
-      // Note: In a real scenario, you'd navigate via search or direct URL
-      // For now, we'll construct the URL pattern
-      await page.goto("/care-standards-tribunal-weekly-hearing-list?artefactId=test");
-      await page.waitForTimeout(1000);
+      await navigateToPublishedList(page, artefactId);

       // Verify page loads (may need adjustment based on actual implementation)
       const heading = page.locator("h1");
       await expect(heading).toBeVisible();
     });
🧹 Nitpick comments (6)
e2e-tests/tests/care-standards-tribunal-upload.spec.ts (6)

166-167: Avoid waitForTimeout — prefer explicit wait conditions.

Hard-coded timeouts are flaky. Use waitForLoadState('networkidle'), waitForSelector, or Playwright's auto-waiting assertions instead.

Example replacement
-  await page.goto("/non-strategic-upload?locationId=9001");
-  await page.waitForTimeout(1000);
+  await page.goto("/non-strategic-upload?locationId=9001");
+  await page.waitForLoadState("networkidle");
-  await page.goto(`/care-standards-tribunal-weekly-hearing-list?artefactId=${artefactId}`);
-  await page.waitForTimeout(1000);
+  await page.goto(`/care-standards-tribunal-weekly-hearing-list?artefactId=${artefactId}`);
+  await page.waitForLoadState("networkidle");

Also applies to: 223-224, 464-465, 475-476, 493-494, 510-511, 524-525, 528-529, 601-601


13-16: Non-null assertions on environment variables risk unclear runtime errors.

If SSO_TEST_SYSTEM_ADMIN_EMAIL or SSO_TEST_SYSTEM_ADMIN_PASSWORD is undefined, the test will fail with an obscure error. Consider validating upfront or throwing a descriptive error.

Proposed improvement
   if (page.url().includes("login.microsoftonline.com")) {
-    const systemAdminEmail = process.env.SSO_TEST_SYSTEM_ADMIN_EMAIL!;
-    const systemAdminPassword = process.env.SSO_TEST_SYSTEM_ADMIN_PASSWORD!;
+    const systemAdminEmail = process.env.SSO_TEST_SYSTEM_ADMIN_EMAIL;
+    const systemAdminPassword = process.env.SSO_TEST_SYSTEM_ADMIN_PASSWORD;
+    if (!systemAdminEmail || !systemAdminPassword) {
+      throw new Error("SSO_TEST_SYSTEM_ADMIN_EMAIL and SSO_TEST_SYSTEM_ADMIN_PASSWORD must be set");
+    }
     await loginWithSSO(page, systemAdminEmail, systemAdminPassword);
   }

166-166: Extract magic numbers into named constants.

Values like "9001" (locationId) and "9" (listTypeId) appear multiple times. Per coding guidelines, use SCREAMING_SNAKE_CASE constants.

Proposed improvement
const CST_LOCATION_ID = "9001";
const CST_LIST_TYPE_ID = "9";

Then reference these throughout:

-  await page.goto("/non-strategic-upload?locationId=9001");
+  await page.goto(`/non-strategic-upload?locationId=${CST_LOCATION_ID}`);
...
-  await page.selectOption('select[name="listType"]', "9");
+  await page.selectOption('select[name="listType"]', CST_LIST_TYPE_ID);

Also applies to: 169-170


353-356: Prefer semantic locators over escaped CSS class selectors.

".govuk-body.govuk-\\!-font-weight-bold" is brittle. Consider using getByText() or a more semantic approach aligned with selector priority guidelines.

Alternative approach
// Instead of relying on class escaping:
const weekCommencingText = await page.getByText(/List for week commencing/i).textContent();

Also applies to: 364-365


358-368: Simplify "Last updated" verification with a targeted locator.

Iterating over all .govuk-body paragraphs is fragile. Use a more direct approach.

Proposed simplification
const lastUpdatedText = page.getByText(/Last updated \d{1,2} \w+ \d{4} at \d{1,2}/);
await expect(lastUpdatedText).toBeVisible();

542-545: Document why target-size and link-name rules are disabled.

Disabling axe-core rules without explanation may mask genuine accessibility issues. Add a comment explaining the rationale.

Also applies to: 554-557, 566-569

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1bc20b1 and 07a3ca0.

📒 Files selected for processing (1)
  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use camelCase for TypeScript variables: userId, caseDetails, documentId
Use PascalCase for classes and interfaces: UserService, CaseRepository. NO I prefix.
Use kebab-case for file and directory names: user-service.ts, case-management/
Use boolean variable names with is/has/can prefix: isActive, hasAccess, canEdit
Module ordering: constants at the top, exported functions next, other functions in order of use, interfaces and types at the bottom
Always add .js extension to relative imports in ES modules (e.g., import { foo } from "./bar.js")
Use workspace aliases for imports (@hmcts/*) instead of relative paths across packages
Use strict TypeScript mode enabled; no any without justification
Use parameterized database queries with Prisma (no raw SQL string concatenation)
Do not include sensitive data in logs

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

Use SCREAMING_SNAKE_CASE for constants: MAX_FILE_SIZE, DEFAULT_TIMEOUT

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
**/*.{test,spec}.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Test files must be co-located with source code using *.test.ts or *.spec.ts naming pattern

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
e2e-tests/**/*.spec.ts

📄 CodeRabbit inference engine (CLAUDE.md)

e2e-tests/**/*.spec.ts: E2E tests must be located in e2e-tests/ directory with *.spec.ts naming pattern
Tag nightly-only E2E tests with @nightly in the test title
E2E tests should use selectors in priority order: getByRole(), getByLabel(), getByText(), getByTestId()
Accessibility testing (WCAG 2.2 AA) is mandatory - include axe-core checks in E2E tests
Include validation checks, Welsh translation checks, accessibility checks, and keyboard navigation within a single E2E test journey
Do not test visual styling (fonts, colors, margins, padding) in E2E tests

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Input validation must be performed on all endpoints

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
**/*.{ts,tsx,js,mjs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not use CommonJS - ES modules only with import/export syntax

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
🧠 Learnings (10)
📓 Common learnings
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Welsh translations are required for all user-facing text
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : Include validation checks, Welsh translation checks, accessibility checks, and keyboard navigation within a single E2E test journey

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : Accessibility testing (WCAG 2.2 AA) is mandatory - include axe-core checks in E2E tests

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : E2E tests must be located in `e2e-tests/` directory with `*.spec.ts` naming pattern

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : Do not test visual styling (fonts, colors, margins, padding) in E2E tests

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : E2E tests should use selectors in priority order: getByRole(), getByLabel(), getByText(), getByTestId()

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : Tag nightly-only E2E tests with `nightly` in the test title

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to libs/*/src/pages/**/*.ts : Provide both `en` and `cy` language objects in page controllers for English and Welsh support

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Welsh translations are required for all user-facing text

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-11-20T09:59:16.776Z
Learnt from: junaidiqbalmoj
Repo: hmcts/cath-service PR: 106
File: libs/system-admin-pages/src/pages/reference-data-upload/index.test.ts:84-160
Timestamp: 2025-11-20T09:59:16.776Z
Learning: In the cath-service repository, Welsh localization (lng=cy) is not required for admin screens (system-admin-pages), so locale preservation in admin screen redirects is not necessary.

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: SonarQube Analysis
  • GitHub Check: E2E Tests
🔇 Additional comments (2)
e2e-tests/tests/care-standards-tribunal-upload.spec.ts (2)

597-650: Good coverage of Welsh translations.

The Welsh language test comprehensively verifies page title, date labels, accordion summary, table headers, data source, back-to-top link, and search title. This aligns well with the requirement that Welsh translations are required for all user-facing text. Based on learnings, this coverage is essential.


536-572: Accessibility checks using axe-core are properly implemented.

WCAG 2.2 AA compliance checks are included for upload form, error page, and list display page as required by coding guidelines.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
e2e-tests/tests/care-standards-tribunal-upload.spec.ts (1)

349-374: Prefer role/text selectors over CSS locators.

To align with the selector priority and improve resilience, use getByRole/getByText (or locator.filter({ hasText })) instead of CSS selectors and manual loops for the heading and “Last updated” checks. As per coding guidelines, please prefer the selector priority order.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 07a3ca0 and f9d7626.

📒 Files selected for processing (1)
  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use camelCase for TypeScript variables: userId, caseDetails, documentId
Use PascalCase for classes and interfaces: UserService, CaseRepository. NO I prefix.
Use kebab-case for file and directory names: user-service.ts, case-management/
Use boolean variable names with is/has/can prefix: isActive, hasAccess, canEdit
Module ordering: constants at the top, exported functions next, other functions in order of use, interfaces and types at the bottom
Always add .js extension to relative imports in ES modules (e.g., import { foo } from "./bar.js")
Use workspace aliases for imports (@hmcts/*) instead of relative paths across packages
Use strict TypeScript mode enabled; no any without justification
Use parameterized database queries with Prisma (no raw SQL string concatenation)
Do not include sensitive data in logs

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

Use SCREAMING_SNAKE_CASE for constants: MAX_FILE_SIZE, DEFAULT_TIMEOUT

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
**/*.{test,spec}.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Test files must be co-located with source code using *.test.ts or *.spec.ts naming pattern

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
e2e-tests/**/*.spec.ts

📄 CodeRabbit inference engine (CLAUDE.md)

e2e-tests/**/*.spec.ts: E2E tests must be located in e2e-tests/ directory with *.spec.ts naming pattern
Tag nightly-only E2E tests with @nightly in the test title
E2E tests should use selectors in priority order: getByRole(), getByLabel(), getByText(), getByTestId()
Accessibility testing (WCAG 2.2 AA) is mandatory - include axe-core checks in E2E tests
Include validation checks, Welsh translation checks, accessibility checks, and keyboard navigation within a single E2E test journey
Do not test visual styling (fonts, colors, margins, padding) in E2E tests

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Input validation must be performed on all endpoints

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
**/*.{ts,tsx,js,mjs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not use CommonJS - ES modules only with import/export syntax

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
🧠 Learnings (10)
📓 Common learnings
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Welsh translations are required for all user-facing text
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : Include validation checks, Welsh translation checks, accessibility checks, and keyboard navigation within a single E2E test journey

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : Accessibility testing (WCAG 2.2 AA) is mandatory - include axe-core checks in E2E tests

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : E2E tests must be located in `e2e-tests/` directory with `*.spec.ts` naming pattern

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : Do not test visual styling (fonts, colors, margins, padding) in E2E tests

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : Tag nightly-only E2E tests with `nightly` in the test title

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : E2E tests should use selectors in priority order: getByRole(), getByLabel(), getByText(), getByTestId()

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to libs/*/src/pages/**/*.ts : Provide both `en` and `cy` language objects in page controllers for English and Welsh support

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Welsh translations are required for all user-facing text

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-11-20T09:59:16.776Z
Learnt from: junaidiqbalmoj
Repo: hmcts/cath-service PR: 106
File: libs/system-admin-pages/src/pages/reference-data-upload/index.test.ts:84-160
Timestamp: 2025-11-20T09:59:16.776Z
Learning: In the cath-service repository, Welsh localization (lng=cy) is not required for admin screens (system-admin-pages), so locale preservation in admin screen redirects is not necessary.

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: E2E Tests
  • GitHub Check: Test Changed Packages
🔇 Additional comments (10)
e2e-tests/tests/care-standards-tribunal-upload.spec.ts (10)

220-227: Navigation helper looks solid.

Waiting for the main heading after navigation is a sensible sync point.


376-404: Accordion default-open checks look good.

The open-attribute check plus content assertions give solid coverage.


407-434: Table content assertions are clear.

Good coverage of headers and representative row data.


436-447: English data source assertion is fine.

Nice, concise validation of source text.


449-461: Welsh data source assertion is fine.

Clear check for the translated data source text.


464-485: Back‑to‑top behaviour is well covered.

The scroll, link, and position assertions are appropriate.


489-507: Search highlight coverage looks good.

Confirms highlighting without hiding rows, which matches the intended behaviour.


509-521: Multi‑column match check is OK.

Good to see coverage outside the case name column.


523-538: Clear‑search behaviour is covered.

The reset path is properly exercised.


603-657: Welsh content assertions are comprehensive.

This is a strong set of checks for translated labels and content.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread e2e-tests/tests/care-standards-tribunal-upload.spec.ts Outdated
Comment on lines 567 to +576
test("should meet WCAG 2.2 AA standards on list display page", async ({ page }) => {
// Placeholder - implement once list display is available
// await page.goto("/care-standards-tribunal-weekly-hearing-list?artefactId=test");
//
// const accessibilityScanResults = await new AxeBuilder({ page })
// .withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22aa"])
// .disableRules(["target-size", "link-name"])
// .analyze();
//
// expect(accessibilityScanResults.violations).toEqual([]);
const artefactId = await completeCSTUploadFlow(page);
await navigateToPublishedList(page, artefactId);

const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22aa"])
.disableRules(["target-size", "link-name"])
.analyze();

expect(accessibilityScanResults.violations).toEqual([]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

wc -l e2e-tests/tests/care-standards-tribunal-upload.spec.ts

Repository: hmcts/cath-service

Length of output: 120


🏁 Script executed:

cat -n e2e-tests/tests/care-standards-tribunal-upload.spec.ts

Repository: hmcts/cath-service

Length of output: 31594


Consolidate validation, Welsh, accessibility, and keyboard navigation into a single E2E test journey.

The E2E guidance requires these checks within one journey, but they are currently split across separate describe blocks. Create a unified test that:

  • Completes the CST upload flow (capturing validation)
  • Runs Axe accessibility scan (WCAG 2.2 AA)
  • Verifies keyboard navigation through the journey
  • Confirms Welsh language support throughout

This consolidation ensures all compliance checks are exercised in a realistic user workflow rather than in isolation.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
e2e-tests/tests/care-standards-tribunal-upload.spec.ts (1)

302-315: Use the real artefactId instead of the hard-coded “test”.

This test discards the returned artefactId and navigates to a fixed URL, which will fail unless a matching artefact exists. Capture the ID and reuse the helper for navigation.

Suggested fix
-    await completeCSTUploadFlow(page);
-
-    // Navigate to the published list
-    // Note: In a real scenario, you'd navigate via search or direct URL
-    // For now, we'll construct the URL pattern
-    await page.goto("/care-standards-tribunal-weekly-hearing-list?artefactId=test");
-    await page.waitForTimeout(1000);
+    const artefactId = await completeCSTUploadFlow(page);
+    await navigateToPublishedList(page, artefactId);
♻️ Duplicate comments (2)
e2e-tests/tests/care-standards-tribunal-upload.spec.ts (2)

318-695: E2E compliance checks are split across multiple journeys.

Guidance requires validation, Welsh translation, accessibility, and keyboard navigation checks in a single end‑to‑end journey, but they are currently separated. Please consolidate into one journey to meet the requirement. Based on learnings, please unify these checks in one test flow.


199-223: ArtefactId derivation still looks incorrect.

The helper returns uploadId as artefactId, but the confirm step typically creates a new artefact. This risks navigating to a non-existent list. Please extract the actual artefactId from the confirm response or created record instead of reusing uploadId.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f9d7626 and 50b1290.

📒 Files selected for processing (1)
  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use camelCase for TypeScript variables: userId, caseDetails, documentId
Use PascalCase for classes and interfaces: UserService, CaseRepository. NO I prefix.
Use kebab-case for file and directory names: user-service.ts, case-management/
Use boolean variable names with is/has/can prefix: isActive, hasAccess, canEdit
Module ordering: constants at the top, exported functions next, other functions in order of use, interfaces and types at the bottom
Always add .js extension to relative imports in ES modules (e.g., import { foo } from "./bar.js")
Use workspace aliases for imports (@hmcts/*) instead of relative paths across packages
Use strict TypeScript mode enabled; no any without justification
Use parameterized database queries with Prisma (no raw SQL string concatenation)
Do not include sensitive data in logs

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

Use SCREAMING_SNAKE_CASE for constants: MAX_FILE_SIZE, DEFAULT_TIMEOUT

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
**/*.{test,spec}.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Test files must be co-located with source code using *.test.ts or *.spec.ts naming pattern

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
e2e-tests/**/*.spec.ts

📄 CodeRabbit inference engine (CLAUDE.md)

e2e-tests/**/*.spec.ts: E2E tests must be located in e2e-tests/ directory with *.spec.ts naming pattern
Tag nightly-only E2E tests with @nightly in the test title
E2E tests should use selectors in priority order: getByRole(), getByLabel(), getByText(), getByTestId()
Accessibility testing (WCAG 2.2 AA) is mandatory - include axe-core checks in E2E tests
Include validation checks, Welsh translation checks, accessibility checks, and keyboard navigation within a single E2E test journey
Do not test visual styling (fonts, colors, margins, padding) in E2E tests

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Input validation must be performed on all endpoints

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
**/*.{ts,tsx,js,mjs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not use CommonJS - ES modules only with import/export syntax

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
🧠 Learnings (10)
📓 Common learnings
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Welsh translations are required for all user-facing text
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : Include validation checks, Welsh translation checks, accessibility checks, and keyboard navigation within a single E2E test journey

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : Accessibility testing (WCAG 2.2 AA) is mandatory - include axe-core checks in E2E tests

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : E2E tests must be located in `e2e-tests/` directory with `*.spec.ts` naming pattern

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : Tag nightly-only E2E tests with `nightly` in the test title

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : Do not test visual styling (fonts, colors, margins, padding) in E2E tests

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : E2E tests should use selectors in priority order: getByRole(), getByLabel(), getByText(), getByTestId()

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to libs/*/src/pages/**/*.ts : Provide both `en` and `cy` language objects in page controllers for English and Welsh support

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Welsh translations are required for all user-facing text

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-11-20T09:59:16.776Z
Learnt from: junaidiqbalmoj
Repo: hmcts/cath-service PR: 106
File: libs/system-admin-pages/src/pages/reference-data-upload/index.test.ts:84-160
Timestamp: 2025-11-20T09:59:16.776Z
Learning: In the cath-service repository, Welsh localization (lng=cy) is not required for admin screens (system-admin-pages), so locale preservation in admin screen redirects is not necessary.

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: SonarQube Analysis
  • GitHub Check: E2E Tests

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
e2e-tests/tests/care-standards-tribunal-upload.spec.ts (1)

267-331: Combine validation, Welsh, accessibility and keyboard checks into one journey.

These checks are still split across separate describes. The E2E guidance requires them to run within a single user journey (e.g., invalid upload → corrected upload → list page → Axe scan → Welsh switch → keyboard navigation). Based on learnings, please consolidate these checks into one flow.

Also applies to: 521-579, 581-639

🧹 Nitpick comments (1)
e2e-tests/tests/care-standards-tribunal-upload.spec.ts (1)

471-479: Prefer role/label-based selectors for the search input.

Use Playwright’s role/label locators for the search input rather than #case-search-input to align with the selector priority guideline and improve resilience. As per coding guidelines, please prefer getByRole/getByLabel where possible.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 50b1290 and bb96fe3.

📒 Files selected for processing (1)
  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use camelCase for TypeScript variables: userId, caseDetails, documentId
Use PascalCase for classes and interfaces: UserService, CaseRepository. NO I prefix.
Use kebab-case for file and directory names: user-service.ts, case-management/
Use boolean variable names with is/has/can prefix: isActive, hasAccess, canEdit
Module ordering: constants at the top, exported functions next, other functions in order of use, interfaces and types at the bottom
Always add .js extension to relative imports in ES modules (e.g., import { foo } from "./bar.js")
Use workspace aliases for imports (@hmcts/*) instead of relative paths across packages
Use strict TypeScript mode enabled; no any without justification
Use parameterized database queries with Prisma (no raw SQL string concatenation)
Do not include sensitive data in logs

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

Use SCREAMING_SNAKE_CASE for constants: MAX_FILE_SIZE, DEFAULT_TIMEOUT

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
**/*.{test,spec}.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Test files must be co-located with source code using *.test.ts or *.spec.ts naming pattern

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
e2e-tests/**/*.spec.ts

📄 CodeRabbit inference engine (CLAUDE.md)

e2e-tests/**/*.spec.ts: E2E tests must be located in e2e-tests/ directory with *.spec.ts naming pattern
Tag nightly-only E2E tests with @nightly in the test title
E2E tests should use selectors in priority order: getByRole(), getByLabel(), getByText(), getByTestId()
Accessibility testing (WCAG 2.2 AA) is mandatory - include axe-core checks in E2E tests
Include validation checks, Welsh translation checks, accessibility checks, and keyboard navigation within a single E2E test journey
Do not test visual styling (fonts, colors, margins, padding) in E2E tests

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Input validation must be performed on all endpoints

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
**/*.{ts,tsx,js,mjs}

📄 CodeRabbit inference engine (CLAUDE.md)

Do not use CommonJS - ES modules only with import/export syntax

Files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
🧠 Learnings (10)
📓 Common learnings
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Welsh translations are required for all user-facing text
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : Include validation checks, Welsh translation checks, accessibility checks, and keyboard navigation within a single E2E test journey

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : E2E tests must be located in `e2e-tests/` directory with `*.spec.ts` naming pattern

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : Tag nightly-only E2E tests with `nightly` in the test title

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : Accessibility testing (WCAG 2.2 AA) is mandatory - include axe-core checks in E2E tests

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : E2E tests should use selectors in priority order: getByRole(), getByLabel(), getByText(), getByTestId()

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to e2e-tests/**/*.spec.ts : Do not test visual styling (fonts, colors, margins, padding) in E2E tests

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Applies to libs/*/src/pages/**/*.ts : Provide both `en` and `cy` language objects in page controllers for English and Welsh support

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-12-15T13:38:57.497Z
Learnt from: CR
Repo: hmcts/cath-service PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-15T13:38:57.497Z
Learning: Welsh translations are required for all user-facing text

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
📚 Learning: 2025-11-20T09:59:16.776Z
Learnt from: junaidiqbalmoj
Repo: hmcts/cath-service PR: 106
File: libs/system-admin-pages/src/pages/reference-data-upload/index.test.ts:84-160
Timestamp: 2025-11-20T09:59:16.776Z
Learning: In the cath-service repository, Welsh localization (lng=cy) is not required for admin screens (system-admin-pages), so locale preservation in admin screen redirects is not necessary.

Applied to files:

  • e2e-tests/tests/care-standards-tribunal-upload.spec.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: E2E Tests
  • GitHub Check: Test Changed Packages

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +207 to +217
// Navigate to summary of publications to find and click the publication link
await page.goto("/summary-of-publications");
await page.waitForTimeout(1000);

// Find the first (most recent) CST publication link
const publicationLinks = page.locator('.govuk-list a[href*="care-standards-tribunal-weekly-hearing-list?artefactId="]');
await expect(publicationLinks.first()).toBeVisible();

// Click the publication link to navigate to the list page
await publicationLinks.first().click();
await page.waitForLoadState("networkidle");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

wc -l e2e-tests/tests/care-standards-tribunal-upload.spec.ts

Repository: hmcts/cath-service

Length of output: 120


🏁 Script executed:

sed -n '190,230p' e2e-tests/tests/care-standards-tribunal-upload.spec.ts

Repository: hmcts/cath-service

Length of output: 1825


🏁 Script executed:

sed -n '1,50p' e2e-tests/tests/care-standards-tribunal-upload.spec.ts

Repository: hmcts/cath-service

Length of output: 1730


🏁 Script executed:

sed -n '100,190p' e2e-tests/tests/care-standards-tribunal-upload.spec.ts

Repository: hmcts/cath-service

Length of output: 3321


🏁 Script executed:

grep -n "artefactId" e2e-tests/tests/care-standards-tribunal-upload.spec.ts

Repository: hmcts/cath-service

Length of output: 188


🏁 Script executed:

grep -n "uploadCSTExcel\|completeCSTUploadFlowAndNavigate" e2e-tests/tests/care-standards-tribunal-upload.spec.ts

Repository: hmcts/cath-service

Length of output: 1290


🏁 Script executed:

sed -n '200,220p' e2e-tests/tests/care-standards-tribunal-upload.spec.ts

Repository: hmcts/cath-service

Length of output: 994


🏁 Script executed:

grep -n "summary-of-publications\|getByRole\|getByLabel\|getByText" e2e-tests/tests/care-standards-tribunal-upload.spec.ts | head -20

Repository: hmcts/cath-service

Length of output: 829


🏁 Script executed:

grep -B5 -A10 "await page.waitForURL.*non-strategic-upload-success" e2e-tests/tests/care-standards-tribunal-upload.spec.ts

Repository: hmcts/cath-service

Length of output: 1611


Use deterministic selector and follow accessibility guidelines for publication link.

The .first() selection assumes a clean test environment and stable ordering. If prior publications exist or the database is not cleaned between runs, the test selects the wrong publication. Instead, capture the artefactId from the upload response and use it to match the specific publication created in this run. Additionally, the current CSS selector violates E2E guidelines—use getByRole() or getByLabel() if the link has accessible text or ARIA attributes that identify it.

@ChrisS1512
ChrisS1512 changed the base branch from master to feature/VIBE-317-the-rcj-hearing-lists January 20, 2026 18:27
@KianKwa
KianKwa changed the base branch from feature/VIBE-317-the-rcj-hearing-lists to feature/323-third-party-subscription-fulfilment February 13, 2026 14:55
@KianKwa
KianKwa changed the base branch from feature/323-third-party-subscription-fulfilment to master February 13, 2026 14:55
@KianKwa
KianKwa merged commit 3d0d29e into master Feb 13, 2026
4 of 6 checks passed
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants