Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
bebd01e
test(ai-image-editor): run the specs and browser tests against @uploa…
nd0ut Oct 6, 2026
c4422c9
test(ai-image-editor): drop recordRequests() calls whose result is un…
nd0ut Oct 6, 2026
7ce65bf
test(ai-image-editor): require the cancel error in the getFileInfo ab…
nd0ut Oct 6, 2026
c6069f7
test(ai-image-editor): name the request when the emulator has no rout…
nd0ut Oct 6, 2026
7b673f3
test(ai-image-editor): wait for a finished result in the auth-token t…
nd0ut Oct 6, 2026
60c310a
test(ai-image-editor): cover a refused generation in the browser
nd0ut Oct 6, 2026
aaee946
test(ai-image-editor): use UUID-shaped ids for made-up CDN sources
nd0ut Oct 6, 2026
c5ef71b
test(ai-image-editor): let the uploader finish its locale switch befo…
nd0ut Oct 6, 2026
8ea9619
build: add msw and @mswjs/interceptors for the browser tests
nd0ut Oct 6, 2026
f4f7298
test(ai-image-editor): run the API emulator in the page
nd0ut Oct 6, 2026
6128bf9
test(ai-image-editor): fail unimplemented Uploadcare endpoints instea…
nd0ut Oct 6, 2026
32ef0e6
test(ai-image-editor): name the real reason token signing stays in Node
nd0ut Oct 6, 2026
931f087
docs: describe the in-page emulator for browser e2e
nd0ut Oct 6, 2026
6877dfe
test(ai-image-editor): wire the emulator into the page with @uploadca…
nd0ut Oct 6, 2026
480eadb
docs: point the browser e2e notes at setupEmulator()
nd0ut Oct 6, 2026
407685b
docs: note the emulator fails Uploadcare hosts it doesn't emulate
nd0ut Oct 6, 2026
2d1e6db
test(ai-image-editor): steer the emulator with presets instead of mag…
nd0ut Oct 7, 2026
60044fa
test(ai-image-editor): read sent requests off emulatorFetch instead o…
nd0ut Oct 7, 2026
4d04ea8
test(ai-image-editor): mint tokens and name seeded files with the emu…
nd0ut Oct 7, 2026
4d63f19
docs: keep the file: dependency note in AGENTS.md only
nd0ut Oct 7, 2026
d6dbaa2
test(ai-image-editor): run the abort-on-source-change e2e against the…
nd0ut Oct 7, 2026
94676be
test(ai-image-editor): time out polls with a session scenario instead…
nd0ut Oct 7, 2026
21118f5
test(ai-image-editor): edit a seeded image in the outputFilename reso…
nd0ut Oct 7, 2026
171c4d5
chore: ignore vitest failure screenshots in every test directory
nd0ut Oct 7, 2026
b8c54bf
test(ai-image-editor): drive specs with the emulator's createFetch an…
nd0ut Oct 7, 2026
e152b73
test(ai-image-editor): run the specs against the emulator's ./node setup
nd0ut Oct 7, 2026
44773a7
refactor(ai-image-editor): drop the test-only fetch option from the A…
nd0ut Oct 7, 2026
228a204
test(ai-image-editor): drop the happy-dom same-origin override from t…
nd0ut Oct 7, 2026
0429bec
test(ai-image-editor): stop the spec emulator after each file
nd0ut Oct 7, 2026
d85871e
build: move to msw 3
nd0ut Oct 8, 2026
7d3024a
test(ai-image-editor): serve msw 3's worker script for the e2e project
nd0ut Oct 8, 2026
c26e05a
build: move to vite 8
nd0ut Oct 8, 2026
f804313
build: move to vite-plugin-dts 5
nd0ut Oct 8, 2026
033b7b4
test(ai-image-editor): prove authToken wiring and caching through a s…
nd0ut Oct 8, 2026
7bc873c
test(ai-image-editor): assert the produced file's name in the filenam…
nd0ut Oct 8, 2026
23aa9fe
test(ai-image-editor): read the sent aspect ratio from the emulator's…
nd0ut Oct 8, 2026
f8a3fd4
test(ai-image-editor): take a run's result uuid from the session's files
nd0ut Oct 8, 2026
6b7d4f8
test(ai-image-editor): drop the fetch-replacing recorder from the e2e…
nd0ut Oct 8, 2026
31feac6
test(ai-image-editor): drop the redundant derivativesInstant from the…
nd0ut Oct 8, 2026
95f6d34
test(ai-image-editor): say why the unset-authToken test expects Signa…
nd0ut Oct 8, 2026
630c5bc
chore: keep Next build output out of Biome
nd0ut Oct 8, 2026
6f00f0b
test(react-ai-image-editor): drop the unused fetch stub from the e2e …
nd0ut Oct 8, 2026
41e61be
test(react-ai-image-editor): run the e2e tests against the in-page em…
nd0ut Oct 8, 2026
b6a17cb
test: restore spies and stubbed globals before every test
nd0ut Oct 9, 2026
014afa6
test(ai-image-editor): remove the canvas tests' Fullscreen API stub a…
nd0ut Oct 9, 2026
4ef7825
test(ai-image-editor): keep the secure-delivery e2e preview on the em…
nd0ut Oct 9, 2026
0d9fb56
test(ai-image-editor): refuse non-Uploadcare origins in the e2e emulator
nd0ut Oct 9, 2026
d454c80
test(react-ai-image-editor): give the gated fallback test a file of i…
nd0ut Oct 9, 2026
483d620
ci: run the tests in a shuffled order
nd0ut Oct 9, 2026
3c077fa
test(ai-image-editor): look up a well-formed absent uuid in the not-f…
nd0ut Oct 9, 2026
dcf6d5d
test(ai-image-editor): abort a hanging client request instead of spyi…
nd0ut Oct 9, 2026
6a4af7f
test(ai-image-editor): abort a hanging derivative job instead of spyi…
nd0ut Oct 9, 2026
ca17661
test(ai-image-editor): script the status frames the polling test walks
nd0ut Oct 9, 2026
967e7d2
test(ai-image-editor): check the derivative provider's results, not i…
nd0ut Oct 9, 2026
1e179bf
test(ai-image-editor): cover the invalid aspect ratios the 1:1 fallba…
nd0ut Oct 9, 2026
5397b4c
test(ai-image-editor): rotate the token between requests in the token…
nd0ut Oct 9, 2026
2dbad8f
test(ai-image-editor): drop the requestUpdate count from the transiti…
nd0ut Oct 9, 2026
644fecd
test(ai-image-editor): test the controller's host registration by wha…
nd0ut Oct 9, 2026
79e100e
test(ai-image-editor): delete the prompt test that asserts its own input
nd0ut Oct 9, 2026
0836981
test(react-ai-image-editor): check that preloadAiImageEditor loads th…
nd0ut Oct 9, 2026
ebd4e93
test(ai-image-editor): pin the default poll interval and timeout with…
nd0ut Oct 9, 2026
586daba
test(react-ai-image-editor): commit the re-render with flushSync inst…
nd0ut Oct 9, 2026
f4cf145
test(ai-image-editor): wait for uc-config's definition, not a 0ms tim…
nd0ut Oct 9, 2026
8bfd388
test(ai-image-editor): wait for the host's resize to land before asse…
nd0ut Oct 9, 2026
9cfe32a
test(ai-image-editor): fail a test whose derivative traffic breaks th…
nd0ut Oct 9, 2026
59cf6b7
test(ai-image-editor): hold the emulator's success frame to the one p…
nd0ut Oct 9, 2026
ec37d98
test(ai-image-editor): check the ratio by the image it produces, not …
nd0ut Oct 9, 2026
23a0938
test(ai-image-editor): run the derivative contract against the emulat…
nd0ut Oct 9, 2026
68df9df
test(ai-image-editor): keep the contract tests out of the browser e2e…
nd0ut Oct 9, 2026
2680a27
fix(a11y): name the aspect-ratio listbox
nd0ut Oct 9, 2026
9c6421a
fix(a11y): announce the selected aspect ratio in the picker's name
nd0ut Oct 9, 2026
3f9fd5c
test(ai-image-editor): find the editor by role in the harness and the…
nd0ut Oct 9, 2026
222759a
test(ai-image-editor): drive the generation tests by role and wait on…
nd0ut Oct 9, 2026
349e9df
test(ai-image-editor): read the history strip's chips, not its entrie…
nd0ut Oct 9, 2026
b6f9756
test(ai-image-editor): delete the Start over absence test, which can'…
nd0ut Oct 9, 2026
46ef97c
test(ai-image-editor): pick ratios by role and click the history chip…
nd0ut Oct 9, 2026
1e02b89
test(ai-image-editor): wait for each authToken run's history chip by …
nd0ut Oct 9, 2026
2a207bc
test(ai-image-editor): assert uc:done fired before reading the commit…
nd0ut Oct 9, 2026
391d7d0
test(ai-image-editor): measure the layout tests on rendered boxes, no…
nd0ut Oct 9, 2026
a5ed0df
test(ai-image-editor): drop the second overlay auto-hide test, a copy…
nd0ut Oct 9, 2026
975b442
test(ai-image-editor): read the rendition off the canvas's images, no…
nd0ut Oct 9, 2026
ef1e73e
test(ai-image-editor): remove the harness's querySelector helpers
nd0ut Oct 9, 2026
841b25d
test(ai-image-editor): find the plugin's editor by role and commit a …
nd0ut Oct 9, 2026
50f4e97
test(ai-image-editor): assert DOM state in the component specs with m…
nd0ut Oct 9, 2026
22094b5
test(ai-image-editor): check uc:select fired before reading it in the…
nd0ut Oct 9, 2026
94407b9
test(ai-image-editor): wait for the sizing tests' styles and boxes we…
nd0ut Oct 9, 2026
c12a12f
test(ai-image-editor): delete the skipped Start over tests
nd0ut Oct 9, 2026
2e89ff2
test(ai-image-editor): un-skip the uc:change test, clearing the resul…
nd0ut Oct 9, 2026
51b717c
test(ai-image-editor): name the seeded images instead of indexing DEM…
nd0ut Oct 9, 2026
99ec951
test(ai-image-editor): assert the provider rejections with rejects, n…
nd0ut Oct 9, 2026
e026c2d
test(ai-image-editor): call the uploader's initFlow unconditionally i…
nd0ut Oct 9, 2026
b83afd5
test(ai-image-editor): check each popular aspect ratio in its own it.…
nd0ut Oct 9, 2026
eecc9d9
test(ai-image-editor): merge the two uc:done detail tests into one
nd0ut Oct 9, 2026
f383d8f
test(ai-image-editor): run the layout placement tables with it.each
nd0ut Oct 9, 2026
660947f
test(ai-image-editor): cover a 5xx status poll in the middle of a job
nd0ut Oct 9, 2026
448ddff
test(ai-image-editor): cover a dropped connection on a status poll mi…
nd0ut Oct 9, 2026
472d1ce
fix(provider): reject a 2xx derivative response that is not a JSON ob…
nd0ut Oct 9, 2026
03a5db2
test(ai-image-editor): cover an unreadable and a mid-job expired auth…
nd0ut Oct 9, 2026
b73042f
test(ai-image-editor): cover an authToken function that throws
nd0ut Oct 9, 2026
abb2608
test(ai-image-editor): property-test the aspect-ratio list parser wit…
nd0ut Oct 9, 2026
4b353ab
test(ai-image-editor): property-test the CDN preview and thumbnail bu…
nd0ut Oct 9, 2026
110ce61
docs(ai-image-editor): note in the e2e harness that preset order matters
nd0ut Oct 9, 2026
d6df806
test(ai-image-editor): read the provider specs' requests by route, no…
nd0ut Oct 9, 2026
ede61ed
test(ai-image-editor): keep the plugin e2e editors off swiftshader WebGL
nd0ut Oct 9, 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: 3 additions & 1 deletion .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ jobs:

- run: npm run build --workspaces --if-present

- run: npm run test --workspaces --if-present
# shuffled so a test that only passes after another one fails here; the seed is the run id, so a failure
# reproduces locally with `npm test -- --sequence.shuffle --sequence.seed=<run id>`
- run: npm run test --workspaces --if-present -- --sequence.shuffle --sequence.seed=${{ github.run_id }}

next-fixture:
if: startsWith(github.head_ref, 'release-please--')
Expand Down
31 changes: 31 additions & 0 deletions .github/workflows/contract.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: contract

# The derivative API contract tests against the real upload.uploadcare.com.
# Run by hand only (each run spends one AI generation); the same tests run
# against the emulator in every `npm test`. Needs the UC_CONTRACT_PUBLIC_KEY
# secret: a project with AI derivatives enabled and signed uploads off. When
# it is missing the run fails instead of skipping.
on:
workflow_dispatch:

permissions:
contents: read

jobs:
contract:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 24
cache: npm

- run: npm ci

- name: Run the contract tests against the real API
env:
UC_CONTRACT_PUBLIC_KEY: ${{ secrets.UC_CONTRACT_PUBLIC_KEY }}
run: npm run test:contract:live -w @uploadcare/ai-image-editor
58 changes: 58 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,64 @@ packages again would give the plugin two `AuthTokenResolverError` classes:
`instanceof` fails across them and the editor wraps the uploader's error a
second time, pushing the original one level deeper on `cause`.

## Tests talk to the Uploadcare API emulator

`@uploadcare/api-emulator` (a devDependency of `packages/ai-image-editor`)
stands in for the Upload API and CDN; tests should not hand-write Uploadcare
responses.

- **Specs** (happy-dom) run against `setupEmulator()` from
`@uploadcare/api-emulator/node`, started once in `tests/specs/setup.ts`
(the `specs` project's setup file). It answers the global `fetch` and
`node:http(s)` (upload-client's transport, so `getFileInfo` too), refuses
every other origin, and resets the session before every test: import
`session` from there to register a scenario or read what was sent from its
`requests`. The clients call the global `fetch`; a spec that checks what
they pass it uses `vi.spyOn(globalThis, 'fetch')` and restores it after the
test.
- **Browser e2e** run the emulator in the page with
`setupEmulator()` from `@uploadcare/api-emulator/browser` (MSW and
`@mswjs/interceptors` underneath, both devDependencies of the root
`package.json`):
`tests/emulator.ts` emulates Uploadcare's hosts and `cdn.example.com` (the
tests' CDN cname), fails any other Uploadcare host, passes every
non-Uploadcare origin through, and resets the session before every test;
import `session` from there to register a scenario or preset, or to read
what the editor sent (`requests`) and what a run produced (`files`; the
harness's `resultUuid()` answers the one that isn't a `DEMO_FILES` image).
A test that checks what the editor passed `fetch` itself uses
`vi.spyOn(globalThis, 'fetch')` and restores it after the test. The editor
tests apply the `derivativesInstant` preset (`tests/editor/harness.ts`) so
a generation finishes on its first poll instead of after the editor's 1.5s
interval several times over.
- **Contract** tests (`tests/contract/`, the `contract` project) hold the
derivative API to one set of assertions on two targets: the emulator in
every `npm test`, and the real `upload.uploadcare.com` with
`npm run test:contract:live -w @uploadcare/ai-image-editor`
(`UC_CONTRACT_PUBLIC_KEY` required; the run fails without it). CI runs the
live target only by hand (the `contract` workflow).
- In specs, e2e and contract tests, a request or response that fails the
client's dev schemas (`uploadcareApiClient.schemas.dev.ts`) fails the test
(`tests/schema-drift.ts`).
- Steer the emulator per test with `session.on()` and `session.use(preset)`
(`session` from the project's setup file); there are no magic keys,
prompts or uuids.
- Bearer tokens must be real: mint them with the emulator's `mintAuthToken()`
(WebCrypto, so it works in the page too). The images every fresh session
holds are its `DEMO_FILES`.
- A `session.on()` handler runs before the emulator's routes and can answer
any `Response` or a promise that never settles, so a bare non-JSON failure,
a job stuck in `processing` or a poll that hangs is a scenario, not a stub.
Stub `fetch` only where it is simpler, and say why next to it.

The React wrapper's e2e project runs against the same in-page emulator
(`packages/react-ai-image-editor/tests/e2e/setup.ts`), so a request the
element starts making there fails loudly instead of reaching the network.

In both packages the dependency is a TEMPORARY `file:` link to an unreleased
checkout, so `npm ci` only resolves it on that machine; swap it for a version
once the package ships.

## Docs layout

`docs/` is a VitePress site published to GitHub Pages: hand-written guides in
Expand Down
3 changes: 3 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
{
"$schema": "https://biomejs.dev/schemas/2.2.6/schema.json",
"files": {
"includes": ["**", "!**/.next"]
},
"assist": {
"actions": {
"source": {
Expand Down
Loading
Loading