Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3e3d794
first pass code migration
klpoland May 6, 2026
17c4541
remove fragments from migrating code
klpoland May 8, 2026
7ac6673
move deprecated files
klpoland May 8, 2026
1144b15
managers class files and clean-up js
klpoland May 8, 2026
0b98c37
repoint page js, new test coverage
klpoland May 8, 2026
632c5b6
fix ref issues
klpoland May 11, 2026
44f882a
clean-up, clean-up
klpoland May 13, 2026
cad6ec6
move partial templates into sub folders for easier role identificatio…
klpoland May 15, 2026
bf70659
move modal logic to backend view, clean up unused templates
klpoland May 15, 2026
d4d7ff2
replace upload modals on capture list in partials
klpoland May 15, 2026
8624691
add fallow to find dupe code, align alert/message method calls, clean…
klpoland May 18, 2026
e3a9a23
consolidate duplicate code with fallow
klpoland May 20, 2026
d6f7750
consolidate test mocks
klpoland May 20, 2026
3cdef1f
cleanup hanging cross-file dupes
klpoland May 20, 2026
7edb8d2
pull out/break up complex functions into utility function files
klpoland May 20, 2026
c7648c7
remove unused tests
klpoland May 20, 2026
2673cb8
removed wrong template
klpoland May 20, 2026
3a802bf
clean up modal method calls
klpoland May 21, 2026
068d6d8
remove deprecated
klpoland May 21, 2026
3c34af1
move action helpers under actions/
klpoland May 21, 2026
32e67a8
fixing modal/dropdown bugs
klpoland May 21, 2026
124b4e5
add/update tests
klpoland May 22, 2026
e1c0cf9
add fallow cross-file dupe check to precommit hooks, add jest test cr…
klpoland May 22, 2026
b19586f
refactor upload management, remove dead-code, break out classes into …
klpoland May 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .cursor/rules/django_javascript_implementation.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ alwaysApply: true
- Classes should follow DRY principles and avoid duplicating the functionality of existing classes.
- Javascript added to existing templates should ONLY call existing classes to initialize them so their functions are available to run in the template.
- Ignore and NEVER update files in the `deprecated/` folder.
- Differentiation of code use by asset type should be configuration-driven, NOT by the method logic-driven.
- Prefer more generic method implementations over specific, per-case basis implementations which lead to bloat.
- If you notice multiple uses of the same block of code, consider adding a utils or manager method/class to handle that instead of repeating the same code everywhere.
- When creating/naming a method, check if a method by the same name/function/use already exists. If so, point it out and suggest refactoring original method to be more generic.

## Django Template Structure

Expand Down
126 changes: 126 additions & 0 deletions .cursor/skills/jest-test-writing/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
---
name: jest-test-writing
description: Writes and refactors Jest unit tests for browser JS (jsdom), emphasizing behavior-focused tests, shared mocks, and repo conventions. Use when adding or updating tests under gateway/sds_gateway/static/js, fixing failing Jest runs, or when the user asks for Jest test best practices.
---

# Jest test writing (SDS gateway static JS)

## When to apply

- New or changed code under `gateway/sds_gateway/static/js/`
- User asks for Jest patterns, test structure, or coverage for frontend managers/handlers
- CI/local failure from `npm test` in `gateway/`

## Run tests

From `gateway/`:

```bash
npm test
npm run test:watch
npm run test:coverage
```

Config: `sds_gateway/static/js/tests-config/jest.config.js` (jsdom, `clearMocks` + `restoreMocks`).

## File placement

| Rule | Detail |
|------|--------|
| Co-locate | `SomeManager.js` → `__tests__/SomeManager.test.js` in the same folder |
| Naming | `*.test.js` or `*.spec.js` (see `testMatch` in jest config) |
| Skip | Never add tests for `deprecated/` |

## What to test (and what not to)

**Do**

- Public methods and user-visible outcomes (DOM updates, calls to `DOMUtils`, `APIClient`, Bootstrap modal show/hide)
- Branches that encode product rules (permissions denied, missing modal, API error responses)
- Async flows: `await` the method under test, then assert mocks/callbacks

**Avoid**

- Asserting private helpers or internal call order unless order is the contract
- Tests that only `expect(x).toBeDefined()` or mirror the implementation line-for-line
- Copy-pasting 50-line mock trees—extend shared helpers instead

## Shared infrastructure (use first)

Read and reuse before inventing mocks:

| Resource | Path |
|----------|------|
| DOM/API/permissions helpers | `sds_gateway/static/js/tests-config/testHelpers.js` |
| Action-manager setups | `sds_gateway/static/js/__tests__/helpers/actionTestMocks.js` |
| Global env | `sds_gateway/static/js/tests-config/jest.setup.js` |

Common helpers:

- `setupStandardUnitTest({ useModalDomUtils, apiClientOverrides, window, getElementByIdMap })` — resets mocks, stubs `document`, sets `window.DOMUtils`
- `createMockDOMUtils` / `createMockDOMUtilsWithModals`
- `createMockAPIClient`, `createMockPermissionsManager`
- `flushMicrotasks()` — after fire-and-forget promises tied to `setTimeout(0)`
- `installDocumentGetByIdMap({ id: element })` when code uses `getElementById`

For Download/Share/Versioning action tests, prefer `setupDownloadActionTestEnvironment` / patterns in `actionTestMocks.js`.

## Structure template

```javascript
/**
* Jest tests for TargetClass
*/

import { TargetClass } from "../TargetClass.js";
import { setupStandardUnitTest, flushMicrotasks } from "../../tests-config/testHelpers.js";

describe("TargetClass", () => {
beforeEach(() => {
setupStandardUnitTest({ /* opts */ });
// Extra per-suite: bootstrap, document.body, window globals
});

test("describes behavior in plain language", async () => {
// arrange → act → assert
});
});
```

Use `require()` for helpers if the file already uses CommonJS; stay consistent within a file.

## Mocking conventions (this repo)

1. **Bootstrap modals** — set `global.bootstrap.Modal` and `Modal.getInstance` in `beforeEach` (see `ModalManager.test.js`, `actionTestMocks.installBootstrapModalMocks`).
2. **`window.DOMUtils`** — via `setupStandardUnitTest` or explicit assign; use `.mockResolvedValue()` for async UI helpers.
3. **`document`** — prefer `installDocumentGetByIdMap` or minimal stubs from testHelpers; use real `document.createElement` / `body.innerHTML` only when testing DOM wiring.
4. **Module mocks** — `jest.mock("../Dependency.js")` at top level; factory returns minimal surface the unit needs.
5. **Fetch / API** — mock `APIClient` methods or `window.fetch` with `createMockFetchResponse` / resolved shapes `{ success: true }` matching production handlers.

## Async

- Prefer `async/await` in tests over bare `.then()`.
- If code schedules work on microtasks/macrotasks without returning a promise, `await flushMicrotasks()` before assertions.
- Fake timers: use `jest.useFakeTimers()` only when testing timer logic; restore in `afterEach`.

## Assertions

- One logical behavior per test name (readable as a spec sentence).
- Assert on **arguments** to mocks when the contract is “calls X with Y” (`toHaveBeenCalledWith`).
- For errors: assert rejected promise or that `showError` / `showModalError` was invoked with expected message.

## Coverage

Global thresholds (70%) in jest config. New code should not drop coverage on touched files; add tests for new branches rather than excluding files.

## Checklist before finishing

- [ ] Test file lives in `__tests__/` next to source
- [ ] Reused `testHelpers` / `actionTestMocks` where applicable
- [ ] `beforeEach` resets DOM/globals needed for isolation
- [ ] Tests describe behavior, not implementation trivia
- [ ] `npm test` passes from `gateway/`

## More detail

See [reference.md](reference.md) for helper exports and example patterns.
70 changes: 70 additions & 0 deletions .cursor/skills/jest-test-writing/reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Jest helpers reference (gateway static JS)

## testHelpers.js (primary exports)

| Export | Use when |
|--------|----------|
| `setupStandardUnitTest` | Default start of unit test `beforeEach` |
| `createMockDOMUtils` | Class uses `window.DOMUtils` without modals |
| `createMockDOMUtilsWithModals` | Modal loading/error/open/close |
| `createMockAPIClient` | Inject or assign API client mock |
| `createMockPermissionsManager` | Permission-gated actions |
| `installDocumentGetByIdMap` | Code looks up specific element IDs |
| `installMinimalDocumentMocks` / `installDocumentQueryStubs` | Low-level DOM stubs |
| `mergeWindowMocks` | Attach globals (`downloadActionManager`, etc.) |
| `flushMicrotasks` | Settle `setTimeout(0)` chains |
| `createMockFetchResponse` / `mockFetchResolved` | Raw `fetch` tests |
| `installCsrfMetaToken` | `APIClient` / CSRF paths |
| `createPublishingSubmitDomFixture` | Publish flow DOM |
| `createDefaultAssetSearchConfig` | Search handler tests |

`setupStandardUnitTest` always calls `jest.clearAllMocks()` and installs document stubs; pass `useModalDomUtils: true` for action/modal suites.

## actionTestMocks.js

| Export | Use when |
|--------|----------|
| `setupDownloadActionTestEnvironment` | Download action manager tests |
| `installBootstrapModalMocks` | Any Bootstrap 5 modal interaction |
| `createMockDownloadPermissions` | Download permission checks |
| `createDefaultShareActionConfig` | Share action defaults |

## Example: permission denied

```javascript
setupStandardUnitTest({
apiClientOverrides: { post: jest.fn().mockResolvedValue({ success: false, message: "Forbidden" }) },
});
const manager = new SomeActionManager({ permissions: { canShare: false } });
await manager.submit();
expect(window.DOMUtils.showError).toHaveBeenCalled();
```

## Example: DOM id map

```javascript
const form = createMockFormElement();
setupStandardUnitTest({
getElementByIdMap: { "share-form": form },
});
```

## jest.setup.js

Provides baseline `document`, `window`, `fetch`, storage, and timers. Do not duplicate full window mocks in every file—override only what the test needs in `beforeEach`.

## Import paths

From `actions/__tests__/Foo.test.js`:

```javascript
const { setupStandardUnitTest } = require("../../tests-config/testHelpers.js");
```

From `actions/details/__tests__/Bar.test.js`:

```javascript
const { setupStandardUnitTest } = require("../../../tests-config/testHelpers.js");
```

Adjust `../` depth to reach `tests-config/`.
4 changes: 4 additions & 0 deletions .github/workflows/gwy-code-quality.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ jobs:
key: prek-gateway-${{ hashFiles('gateway/.pre-commit-config.yaml') }}
path: ~/.cache/prek/

- name: Install gateway frontend dependencies
working-directory: ./gateway
run: npm ci

- name: Install hooks
working-directory: ./gateway
run: uv run --extra local prek install --install-hooks
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/sdk-code-quality.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ jobs:
key: prek-${{ hashFiles('.pre-commit-config.yaml') }}
path: ~/.cache/prek/

- name: Install gateway frontend dependencies
working-directory: ./gateway
run: npm ci

- name: Install hooks
working-directory: ./sdk
run: uv run --dev prek install --install-hooks
Expand Down
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ repos:
language: system
pass_filenames: false # run once per commit, not per file
types: [ python ]
- id: fallow-cross-file-dupes
name: fallow cross-file dupes (gateway static js)
entry: bash gateway/scripts/fallow-cross-file-dupes.sh
language: system
pass_filenames: false
files: ^gateway/sds_gateway/static/js/.*\.js$

# sets up .pre-commit-ci.yaml to ensure pre-commit dependencies stay up to date
ci:
Expand Down
9 changes: 9 additions & 0 deletions gateway/.fallowrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"$schema": "https://raw.githubusercontent.com/fallow-rs/fallow/main/schema.json",
"ignorePatterns": [
"sds_gateway/static/js/deprecated/**",
"compose/**",
"sds_gateway/static/css/**"
],
"ignoreExportsUsedInFile": true
}
2 changes: 2 additions & 0 deletions gateway/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,8 @@ tags
dump.rdb

### Project template
.fallow/

sds_gateway/media/

.pytest_cache/
Expand Down
Loading
Loading