test(image-editor): stop the full-resolution export spec from timing out under load - #71
Merged
Merged
Conversation
…out under load
The spec pushed a 2400 x 2000 image through several full-resolution browser
steps. Chrome encodes canvas.toBlob() PNG on the main thread in idle time only
(it waits up to 1 s to start and up to 5.7 s more to finish), and image
decodes can stall on a loaded machine. The spec takes about 0.5 s on an idle
machine but went past the 5 s default in full-suite runs, both locally and in
CI (3 of the 4 red CI runs on 2026-09-29/30).
- Write the large source as PNG bytes (grayPng) instead of encoding a canvas.
It is opaque, so the editor also skips its transparency scan.
- Read back only the pixels the spec checks (decode(blob, region)). The
assertions are unchanged, and the decoded size is now asserted too.
- Give this spec an explicit 30 s budget for its inherent 4.8 MP work.
- Probe the encodable formats once in beforeAll, so that cost never lands on
whichever spec loads an image first.
A timed-out spec kept running and, through the module-level fixture/host,
failed the next spec ("editor not rendered", "Expected $[0] = 60 to equal
2400"), because Jasmine reports late errors and expectations on the spec that
is running. stopPolling() in afterEach now parks every until() left over from
a finished spec.
Verification: focused image-editor specs 114/114; full v19 suite 5970/5970
twice (seeds 10960, 82141); npm run check:sync passed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
SdImageEditor export exports from the full-resolution source, not the downscaled previewtimed out at 5 s in full-suite runs, both locally and in CI. It caused 3 of the 4 red CI runs on 2026-09-29/30 (runs 36562656580, 36666512326 and 36693626376, all on commits that never touched the image editor). When it timed out, its leftover async code also failed the next spec, witheditor not rendered,Expected 'loading' to be 'ready'orExpected $[0] = 60 to equal 2400.This PR makes the spec independent of machine load without changing what it proves, and stops a timed-out spec from failing another one. Only spec files change. There is no library or public API change, so there is no changelog entry.
Root cause
canvas.toBlob()PNG on the main thread in idle time only: with no idle time it waits up to 1 s to start and up to 5.7 s more to finish. Image decodes also stall on a loaded machine.createImageBitmapstalled for 6 s; 6.7 s when the main thread was kept busy on purpose, which reproduces the timeout.jasmine.clock().install()is undone in afinally) and missing teardown (no leaked intervals, animation-frame loops or CSS animations were running).fixture/host. Jasmine 5.5 reports late errors and expectations on whichever spec is running at that moment.Changes
image-editor.fixtures.spec.tsgrayPng()writes a large source as PNG bytes withCompressionStream, so there is no idle-time canvas encode. The image is opaque, so the editor also skips its transparency scan. The browser still decodes it.decode(blob, region)reads back only a region.pixel()now throws for a pixel that was not read back.stopPolling()parks everyuntil()left over from a finished spec, instead of letting it throw into the next one.image-editor.component.spec.tsgrayPng()and a 3 × 1 region read. Its assertions are unchanged, and it now also asserts the decoded size (2400 × 2000).LARGE_IMAGE_TIMEOUT) for its inherent 4.8 MP work. This is a budget, not a blind bump: with no idle time the new version takes up to about 4 s, and one decode stalled for 6 s in a full-suite run.beforeAllprobes the encodable formats once, so that cost never lands on whichever spec loads an image first.afterEachcallsstopPolling().npm run sync. TheSYNC-STATUS.mdtimestamps are refreshed, and the v22 files are LF.Verification
On this HEAD (8f57269, rebased on main 6e0509a), Node 22.22.3:
npx ng test sdcorejs-angular --watch=false --browsers=ChromeHeadlessCI --code-coverage(v19): 6168/6168 passed, seed 240905. Coverage gate passed: 87.61 / 76.54 / 88.07 / 89.93.npm run lint(v19): all files pass.npm run check:sync: passed.Before the rebase, on the same change:
Not run locally: the v20, v21 and v22 Karma suites (the compatibility jobs cover them), and root
test:scripts(no script reads these specs).Risks and rollback
stopPolling()covers leftover polls; leftover code paused somewhere else could still touch the next spec's fixture.Follow-ups (not in this PR)
image-editor source decoding reports a truncated file as decode-failedtimed out on its own in CI run 36605910408.🤖 Generated with Claude Code