@delacour/warden-e2e 0.1.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.mjs CHANGED
@@ -1,33 +1,37 @@
1
1
  // @bun
2
2
  import {
3
- BUN_TIMEOUT_MARGIN_MS,
4
- E2eError,
5
- WARDEN_E2E_VERSION,
6
- attemptDir,
7
- attemptSession,
8
- callerFile,
3
+ E2eError2,
9
4
  canObserve,
10
- createScope,
11
- currentScope,
12
5
  fieldValue,
13
- flowIdOf,
14
- getSession,
15
- hash6,
16
- hooksFor,
17
- isLocator,
18
- markOnly,
19
- matchesText,
20
6
  normalizeText,
21
- pickOne,
22
- registrationDefaults,
23
- renderHeader,
7
+ matchesText,
24
8
  renderSnapshot,
9
+ renderHeader,
25
10
  resolve,
26
- resolveUse,
27
- runInScope,
11
+ isLocator,
12
+ pickOne,
13
+ textOf,
14
+ isHidLost,
28
15
  runStep,
29
- textOf
30
- } from "./chunk-qtkmfjbq.mjs";
16
+ attemptSession,
17
+ getSession,
18
+ perfStartFor,
19
+ hash6,
20
+ attemptDir,
21
+ specIdOf,
22
+ captureNativeLog,
23
+ callerFile,
24
+ BUN_TIMEOUT_MARGIN_MS2,
25
+ ownHook,
26
+ currentScope,
27
+ createScope,
28
+ runInScope,
29
+ hooksFor,
30
+ resolveUse,
31
+ registrationDefaults,
32
+ markOnly,
33
+ WARDEN_E2E_VERSION2
34
+ } from "./chunk-8em9kgsk.mjs";
31
35
  // ../../packages/e2e/src/expect/expect.ts
32
36
  import { expect as bunExpect } from "bun:test";
33
37
 
@@ -47,22 +51,22 @@ function show(match) {
47
51
  function assertOptions(matcher, options, allowed) {
48
52
  const unknown = Object.keys(options).filter((key) => !allowed.includes(key));
49
53
  if (unknown.length > 0) {
50
- throw new E2eError("INVALID_ARGUMENT", `${matcher} does not take ${unknown.map((key) => `"${key}"`).join(", ")}; it takes ${allowed.join(", ")}`);
54
+ throw new E2eError2("INVALID_ARGUMENT", `${matcher} does not take ${unknown.map((key) => `"${key}"`).join(", ")}; it takes ${allowed.join(", ")}`);
51
55
  }
52
56
  const bag = options;
53
57
  for (const key of ["checked", "exact", "ignoreCase"]) {
54
58
  if (bag[key] !== undefined && typeof bag[key] !== "boolean") {
55
- throw new E2eError("INVALID_ARGUMENT", `${matcher}: "${key}" must be a boolean`);
59
+ throw new E2eError2("INVALID_ARGUMENT", `${matcher}: "${key}" must be a boolean`);
56
60
  }
57
61
  }
58
62
  const timeout = bag.timeout;
59
63
  if (timeout !== undefined && (typeof timeout !== "number" || !Number.isFinite(timeout) || timeout < 0)) {
60
- throw new E2eError("INVALID_ARGUMENT", `${matcher}: "timeout" must be a non-negative number of milliseconds`);
64
+ throw new E2eError2("INVALID_ARGUMENT", `${matcher}: "timeout" must be a non-negative number of milliseconds`);
61
65
  }
62
66
  }
63
67
  function failUnlessRetryable(error) {
64
68
  if (!error.retryable)
65
- throw new E2eError("DRIVER", `snapshot failed: ${error.code}: ${error.message}`);
69
+ throw new E2eError2("DRIVER", `snapshot failed: ${error.code}: ${error.message}`);
66
70
  }
67
71
  function regexTest(pattern, value, ignoreCase = false) {
68
72
  const flags = pattern.flags.replaceAll(/[gy]/g, "");
@@ -144,7 +148,7 @@ class LocatorAssertions {
144
148
  if (entry === undefined)
145
149
  return missing();
146
150
  if (entry.secure) {
147
- throw new E2eError("INVALID_ARGUMENT", "toHaveValue cannot judge a secure field: its value is withheld. Assert the outcome of the fill instead.");
151
+ throw new E2eError2("INVALID_ARGUMENT", "toHaveValue cannot judge a secure field: its value is withheld. Assert the outcome of the fill instead.");
148
152
  }
149
153
  const value = fieldValue(entry);
150
154
  const matches = typeof expected === "string" ? value === expected : regexTest(expected, value);
@@ -188,7 +192,7 @@ class LocatorAssertions {
188
192
  async toHaveCount(expected, options = {}) {
189
193
  assertOptions("toHaveCount", options, ["timeout"]);
190
194
  if (!Number.isInteger(expected) || expected < 0) {
191
- throw new E2eError("INVALID_ARGUMENT", `toHaveCount takes a non-negative integer, got ${String(expected)}`);
195
+ throw new E2eError2("INVALID_ARGUMENT", `toHaveCount takes a non-negative integer, got ${String(expected)}`);
192
196
  }
193
197
  return this.assert({
194
198
  call: `toHaveCount(${expected})`,
@@ -203,7 +207,7 @@ class LocatorAssertions {
203
207
  try {
204
208
  await rt.step(`expect ${this.locator}.${spec.call}`, () => this.poll(spec, timeoutMs));
205
209
  } catch (error) {
206
- if (this.context.soft !== undefined && error instanceof E2eError && error.code !== "TIMEOUT") {
210
+ if (this.context.soft !== undefined && error instanceof E2eError2 && error.code !== "TIMEOUT") {
207
211
  this.context.soft(error);
208
212
  return;
209
213
  }
@@ -255,7 +259,7 @@ class LocatorAssertions {
255
259
  `)}
256
260
 
257
261
  ${screen}`;
258
- return new E2eError("ASSERTION", message, screen);
262
+ return new E2eError2("ASSERTION", message, screen);
259
263
  }
260
264
  }
261
265
  function conditionHold(negate, holdMs, now) {
@@ -311,9 +315,9 @@ function currentSoftSink() {
311
315
  }
312
316
  var valueExpect = bunExpect;
313
317
  function asAssertion(error) {
314
- if (error instanceof E2eError)
318
+ if (error instanceof E2eError2)
315
319
  return error;
316
- return new E2eError("ASSERTION", error instanceof Error ? error.message : String(error));
320
+ return new E2eError2("ASSERTION", error instanceof Error ? error.message : String(error));
317
321
  }
318
322
  function isThenable(value) {
319
323
  return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
@@ -469,6 +473,19 @@ async function captureDevtoolsArtifacts(facet, dir, timeoutMs = CAPTURE_TIMEOUT_
469
473
  }
470
474
  return capture;
471
475
  }
476
+ function nativeTarget(env) {
477
+ return env.platform === "ios" ? { platform: "ios", udid: env.udid, bundleId: env.bundleId } : { platform: "android", serial: env.udid, pkg: env.bundleId };
478
+ }
479
+ async function captureNativeArtifacts(target, deps, win, timeoutMs = CAPTURE_TIMEOUT_MS, budget = unlimited) {
480
+ const ms = Math.min(timeoutMs, budget());
481
+ if (ms <= 0)
482
+ return { files: [], problems: [`native log: ${SPENT}`] };
483
+ try {
484
+ return await limited(captureNativeLog(target, deps, { ...win, nowMs: Date.now(), timeoutMs: ms }), ms);
485
+ } catch (cause) {
486
+ return { files: [], problems: [`native log: ${message(cause)}`] };
487
+ }
488
+ }
472
489
 
473
490
  // ../../packages/e2e/src/runner/trace.ts
474
491
  import { appendFileSync, mkdirSync as mkdirSync2 } from "fs";
@@ -582,7 +599,7 @@ var POST_DEADLINE_RESERVE_MS = 5000;
582
599
  var DEFAULT_TIMING = {
583
600
  cleanupTimeoutMs: CLEANUP_TIMEOUT_MS,
584
601
  captureTimeoutMs: CAPTURE_TIMEOUT_MS,
585
- bunMarginMs: BUN_TIMEOUT_MARGIN_MS,
602
+ bunMarginMs: BUN_TIMEOUT_MARGIN_MS2,
586
603
  reserveMs: POST_DEADLINE_RESERVE_MS
587
604
  };
588
605
  var timing = { ...DEFAULT_TIMING };
@@ -594,7 +611,7 @@ function unavailable(name) {
594
611
  get(_target, prop) {
595
612
  if (typeof prop === "symbol" || prop === "then")
596
613
  return;
597
- throw new E2eError("SESSION", `the \`${name}\` fixture is not available: no fixture factory is registered on the session`);
614
+ throw new E2eError2("SESSION", `the \`${name}\` fixture is not available: no fixture factory is registered on the session`);
598
615
  }
599
616
  });
600
617
  }
@@ -627,12 +644,12 @@ async function startFixture(entry, bag) {
627
644
  return;
628
645
  };
629
646
  let used = false;
630
- const value = new Promise((resolve2, reject) => {
631
- provide = resolve2;
647
+ const value = new Promise((resolve, reject) => {
648
+ provide = resolve;
632
649
  fail = reject;
633
650
  });
634
- const released = new Promise((resolve2) => {
635
- release = resolve2;
651
+ const released = new Promise((resolve) => {
652
+ release = resolve;
636
653
  });
637
654
  const finished = entry.setup(bag, async (v) => {
638
655
  used = true;
@@ -640,7 +657,7 @@ async function startFixture(entry, bag) {
640
657
  await released;
641
658
  }).then(() => {
642
659
  if (!used)
643
- fail(new E2eError("INVALID_ARGUMENT", `fixture \`${entry.name}\` finished without calling use()`));
660
+ fail(new E2eError2("INVALID_ARGUMENT", `fixture \`${entry.name}\` finished without calling use()`));
644
661
  }, (error) => {
645
662
  if (!used)
646
663
  fail(error);
@@ -666,7 +683,7 @@ function cleanupDeadline(startedAt, timeoutMs, bunTimeoutMs) {
666
683
  function limited2(label, ms, fn) {
667
684
  let timer;
668
685
  const timeout = new Promise((_, reject) => {
669
- timer = setTimeout(() => reject(new E2eError("TIMEOUT", `${label} timed out after ${ms}ms`)), ms);
686
+ timer = setTimeout(() => reject(new E2eError2("TIMEOUT", `${label} timed out after ${ms}ms`)), ms);
670
687
  });
671
688
  return Promise.race([fn(), timeout]).finally(() => clearTimeout(timer));
672
689
  }
@@ -681,13 +698,17 @@ function errorInfo(error) {
681
698
  if (error instanceof Error) {
682
699
  return {
683
700
  message: error.message,
684
- ...error instanceof E2eError ? { code: error.code } : {},
701
+ ...error instanceof E2eError2 ? { code: error.code } : {},
685
702
  ...error.stack !== undefined ? { stack: error.stack } : {}
686
703
  };
687
704
  }
688
705
  return { message: String(error) };
689
706
  }
690
707
  async function runAttempt(session, spec) {
708
+ if (await runAttemptOnce(session, spec) === "again")
709
+ await runAttemptOnce(session, { ...spec });
710
+ }
711
+ async function runAttemptOnce(session, spec) {
691
712
  const { env } = session;
692
713
  const use = resolveUse(spec.scope, { timeoutMs: env.timeoutMs, actionTimeoutMs: env.actionTimeoutMs, assertionTimeoutMs: env.assertionTimeoutMs }, spec.timeout);
693
714
  const startedAt = Date.now();
@@ -696,7 +717,7 @@ async function runAttempt(session, spec) {
696
717
  const device = { udid: env.udid, platform: env.platform };
697
718
  const clock = !env.logTimestamps;
698
719
  const short = shortName(spec.file);
699
- const dir = attemptDir(session.out, flowIdOf(spec.file, env.flowsDir), spec.title, hash6(spec.file, ...spec.title));
720
+ const dir = attemptDir(session.out, specIdOf(spec.file, env.specsDir), spec.title, hash6(spec.file, ...spec.title));
700
721
  const print = (line) => {
701
722
  if (line !== undefined)
702
723
  session.log(line);
@@ -721,57 +742,61 @@ async function runAttempt(session, spec) {
721
742
  const bound = attemptSession(session, () => binding ?? { context: ctx, finished: false });
722
743
  print(formatConsoleLine({ type: "test-start", test: { file: spec.file, title: [...spec.title], startedAt, device } }, { clock }));
723
744
  session.enterAttempt(ctx);
745
+ const teardowns = [];
746
+ const state = {};
747
+ let outcome = await raceDeadline(controller, use.timeoutMs, () => setUpAndRun(bound, spec, use, controller.signal, state, teardowns));
748
+ outcome = failIf(outcome, ctx.softFailures.length > 0, () => softFailureError(ctx.softFailures));
749
+ if (outcome.failed)
750
+ await captureFailureArtifacts(session, ctx, budget, startedAt);
751
+ const cleanupErrors = await runCleanup(session, spec, ctx, state.bag, teardowns, budget, (context) => {
752
+ binding = { context, finished: false };
753
+ });
754
+ const finished = new E2eError2("SESSION", "attempt finished");
755
+ if (!controller.signal.aborted)
756
+ controller.abort(finished);
757
+ binding = { context: { ...ctx, signal: AbortSignal.abort(finished), depth: 0 }, finished: true };
758
+ session.leaveAttempt(ctx);
759
+ trace.flush();
760
+ outcome = failIf(outcome, cleanupErrors.length > 0, () => cleanupErrors[0]);
761
+ return concludeAttempt(session, spec, ctx, { startedAt, device }, outcome, print, clock);
762
+ }
763
+ async function raceDeadline(controller, timeoutMs, run) {
724
764
  let deadlineHit = false;
725
765
  const timer = setTimeout(() => {
726
766
  deadlineHit = true;
727
- controller.abort(new E2eError("TIMEOUT", `test timed out after ${use.timeoutMs}ms`));
728
- }, use.timeoutMs);
767
+ controller.abort(new E2eError2("TIMEOUT", `test timed out after ${timeoutMs}ms`));
768
+ }, timeoutMs);
729
769
  const deadline = new Promise((_, reject) => {
730
770
  controller.signal.addEventListener("abort", () => reject(controller.signal.reason), { once: true });
731
771
  });
732
772
  deadline.catch(() => {
733
773
  return;
734
774
  });
735
- const teardowns = [];
736
- const state = {};
737
- let failed = false;
738
- let failure;
739
775
  try {
740
- await Promise.race([setUpAndRun(bound, spec, use, controller.signal, state, teardowns), deadline]);
776
+ await Promise.race([run(), deadline]);
777
+ return { failed: false, failure: undefined, deadlineHit };
741
778
  } catch (error) {
742
- failed = true;
743
- failure = error;
779
+ return { failed: true, failure: error, deadlineHit };
744
780
  } finally {
745
781
  clearTimeout(timer);
746
782
  }
747
- if (!failed && ctx.softFailures.length > 0) {
748
- failed = true;
749
- failure = softFailureError(ctx.softFailures);
750
- }
751
- if (failed)
752
- await captureFailureArtifacts(session, ctx, budget);
753
- const cleanupErrors = await runCleanup(session, spec, ctx, state.bag, teardowns, budget, (context) => {
754
- binding = { context, finished: false };
755
- });
756
- const finished = new E2eError("SESSION", "attempt finished");
757
- if (!controller.signal.aborted)
758
- controller.abort(finished);
759
- binding = { context: { ...ctx, signal: AbortSignal.abort(finished), depth: 0 }, finished: true };
760
- session.leaveAttempt(ctx);
761
- trace.flush();
762
- if (!failed && cleanupErrors.length > 0) {
763
- failed = true;
764
- failure = cleanupErrors[0];
765
- }
766
- for (const shot of listScreenshots(dir))
767
- files.add(shot);
768
- const outcome = failed ? { failed: true, error: failure, timedOut: deadlineHit } : { failed: false };
769
- const result = buildResult(spec, ctx, { startedAt, device }, outcome);
783
+ }
784
+ function failIf(outcome, present, failure) {
785
+ return outcome.failed || !present ? outcome : { ...outcome, failed: true, failure: failure() };
786
+ }
787
+ async function concludeAttempt(session, spec, ctx, run, outcome, print, clock) {
788
+ for (const shot of listScreenshots(ctx.dir))
789
+ ctx.files.add(shot);
790
+ const result = buildResult(spec, ctx, run, outcome.failed ? { failed: true, error: outcome.failure, timedOut: outcome.deadlineHit } : { failed: false });
770
791
  print(formatConsoleLine({ type: "test-end", result }, { clock }));
771
- if (!failed || spec.final)
792
+ if (outcome.failed && session.recoverHid !== undefined && isHidLost(outcome.failure) && await session.recoverHid()) {
793
+ return "again";
794
+ }
795
+ if (!outcome.failed || spec.final)
772
796
  session.results.append(result);
773
- if (failed)
774
- throw failure instanceof Error ? failure : new Error(String(failure));
797
+ if (outcome.failed)
798
+ throw outcome.failure instanceof Error ? outcome.failure : new Error(String(outcome.failure));
799
+ return;
775
800
  }
776
801
  function softFailureError(failures) {
777
802
  if (failures.length === 1 && failures[0] !== undefined)
@@ -779,7 +804,7 @@ function softFailureError(failures) {
779
804
  const body = failures.map((failure, index) => `${index + 1}) ${failure.message}`).join(`
780
805
 
781
806
  `);
782
- return new E2eError("ASSERTION", `${failures.length} soft assertions failed:
807
+ return new E2eError2("ASSERTION", `${failures.length} soft assertions failed:
783
808
 
784
809
  ${body}`, failures[0]?.screen);
785
810
  }
@@ -798,28 +823,43 @@ function buildResult(spec, ctx, run, outcome) {
798
823
  };
799
824
  }
800
825
  async function setUpAndRun(session, spec, use, signal, state, teardowns) {
801
- const base2 = await buildBaseFixtures(session, signal, use);
802
- const built = { ...base2 };
826
+ const base = await buildBaseFixtures(session, signal, use);
827
+ const built = { ...base };
803
828
  state.bag = built;
829
+ const perfNames = session.env.perf;
830
+ if (perfNames !== undefined) {
831
+ const { perf } = base.device;
832
+ await session.step("perf", "perf.start", () => perf.start(perfStartFor(perfNames)));
833
+ teardowns.push({
834
+ name: "perf",
835
+ run: async () => {
836
+ if (perf.active)
837
+ await perf.stop();
838
+ }
839
+ });
840
+ }
804
841
  if (use.launch === "fresh")
805
- await session.step("app", "app.open", () => base2.app.open());
842
+ await session.step("app", "app.open", () => base.app.open());
806
843
  for (const entry of spec.fixtures) {
807
844
  const started = await session.step("fixture", entry.name, () => startFixture(entry, built));
808
845
  built[entry.name] = started.value;
809
846
  teardowns.push({ name: entry.name, run: started.teardown });
810
847
  }
811
- for (const hook of hooksFor(spec.scope, "beforeEach")) {
848
+ for (const hook of hooksFor(spec.scope, "beforeEach", spec.fixtures)) {
812
849
  await session.step("hook", "beforeEach", async () => hook(built));
813
850
  }
814
851
  await spec.body(built);
815
852
  }
816
- async function captureFailureArtifacts(session, ctx, budget) {
853
+ async function captureFailureArtifacts(session, ctx, budget, startedAt) {
817
854
  const driver = session.peekDriver();
818
855
  const facet = session.peekDevtools();
819
856
  const { captureTimeoutMs } = runnerTiming();
820
857
  const captures = [
821
858
  ...driver === undefined ? [] : [await captureFailure(driver, ctx.dir, captureTimeoutMs, budget)],
822
- ...facet === undefined ? [] : [await captureDevtoolsArtifacts(facet, ctx.dir, captureTimeoutMs, budget)]
859
+ ...facet === undefined ? [] : [await captureDevtoolsArtifacts(facet, ctx.dir, captureTimeoutMs, budget)],
860
+ ...session.nativeLog === undefined ? [] : [
861
+ await captureNativeArtifacts(nativeTarget(session.env), session.nativeLog, { dir: ctx.dir, sinceMs: startedAt }, captureTimeoutMs, budget)
862
+ ]
823
863
  ];
824
864
  for (const capture of captures) {
825
865
  for (const file of capture.files)
@@ -840,21 +880,21 @@ async function runCleanup(session, spec, ctx, built, teardowns, budget, bind) {
840
880
  try {
841
881
  const left = budget();
842
882
  if (left <= 0)
843
- throw new E2eError("TIMEOUT", `${label} skipped: the cleanup budget after the test is used up`);
883
+ throw new E2eError2("TIMEOUT", `${label} skipped: the cleanup budget after the test is used up`);
844
884
  await limited2(label, Math.min(timing.cleanupTimeoutMs, left), fn);
845
885
  } catch (error) {
846
886
  errors.push(error);
847
887
  }
848
888
  };
849
889
  if (built !== undefined) {
850
- for (const hook of hooksFor(spec.scope, "afterEach")) {
890
+ for (const hook of hooksFor(spec.scope, "afterEach", spec.fixtures)) {
851
891
  await attempt("afterEach", () => step("hook", "afterEach", async () => hook(built)));
852
892
  }
853
893
  }
854
894
  for (const fixture of [...teardowns].reverse()) {
855
895
  await attempt(`fixture ${fixture.name} teardown`, () => step("fixture", `${fixture.name} teardown`, fixture.run));
856
896
  }
857
- cleanup.abort(new E2eError("SESSION", "attempt finished"));
897
+ cleanup.abort(new E2eError2("SESSION", "attempt finished"));
858
898
  session.leaveAttempt(cleanupCtx);
859
899
  return errors;
860
900
  }
@@ -872,7 +912,7 @@ function registerTest(fixtures, mode, title, second, third) {
872
912
  const options = typeof second === "function" ? {} : second;
873
913
  const body = typeof second === "function" ? second : third;
874
914
  if (body === undefined) {
875
- throw new E2eError("INVALID_ARGUMENT", `test("${title}"): the test body is missing`);
915
+ throw new E2eError2("INVALID_ARGUMENT", `test("${title}"): the test body is missing`);
876
916
  }
877
917
  const scope = currentScope();
878
918
  const file = callerFile();
@@ -910,7 +950,10 @@ function registerTest(fixtures, mode, title, second, third) {
910
950
  bunTest(title, run, bunOptions);
911
951
  }
912
952
  function eraseSetup(setup) {
913
- return (base2, use) => setup(base2, use);
953
+ return (base, use) => setup(base, use);
954
+ }
955
+ function eraseHook(hook) {
956
+ return (fixtures) => hook(fixtures);
914
957
  }
915
958
  function isKeyOf(object, key) {
916
959
  return key in object;
@@ -919,21 +962,27 @@ function makeCallable(fixtures, mode) {
919
962
  return (title, second, third) => registerTest(fixtures, mode, title, second, third);
920
963
  }
921
964
  function makeTest(fixtures) {
922
- const base2 = makeCallable(fixtures, "run");
923
- return Object.assign(base2, {
965
+ const base = makeCallable(fixtures, "run");
966
+ return Object.assign(base, {
924
967
  skip: makeCallable(fixtures, "skip"),
925
968
  only: makeCallable(fixtures, "only"),
926
969
  todo(title) {
927
970
  bunTest.todo(title, () => {
928
- throw new E2eError("INVALID_ARGUMENT", `test.todo("${title}") is not implemented`);
971
+ throw new E2eError2("INVALID_ARGUMENT", `test.todo("${title}") is not implemented`);
929
972
  });
930
973
  },
931
974
  skipIf(condition) {
932
975
  return makeCallable(fixtures, condition ? "skip" : "run");
933
976
  },
977
+ beforeEach(hook) {
978
+ currentScope().beforeEach.push(ownHook(eraseHook(hook), fixtures));
979
+ },
980
+ afterEach(hook) {
981
+ currentScope().afterEach.push(ownHook(eraseHook(hook), fixtures));
982
+ },
934
983
  use(options) {
935
984
  if (options.systemAlerts !== undefined && !SYSTEM_ALERTS.includes(options.systemAlerts)) {
936
- throw new E2eError("INVALID_ARGUMENT", `test.use: systemAlerts must be one of ${SYSTEM_ALERTS.map((mode) => `"${mode}"`).join(", ")}, got ${JSON.stringify(options.systemAlerts)}`);
985
+ throw new E2eError2("INVALID_ARGUMENT", `test.use: systemAlerts must be one of ${SYSTEM_ALERTS.map((mode) => `"${mode}"`).join(", ")}, got ${JSON.stringify(options.systemAlerts)}`);
937
986
  }
938
987
  const scope = currentScope();
939
988
  if (options.timeout !== undefined && registered.has(scope)) {
@@ -957,7 +1006,7 @@ function describe(title, second, third) {
957
1006
  const body = typeof second === "function" ? second : third;
958
1007
  const options = typeof second === "function" ? {} : second;
959
1008
  if (body === undefined)
960
- throw new E2eError("INVALID_ARGUMENT", `describe("${title}"): the body is missing`);
1009
+ throw new E2eError2("INVALID_ARGUMENT", `describe("${title}"): the body is missing`);
961
1010
  const scope = createScope(title, options.serial === true);
962
1011
  bunDescribe(title, () => runInScope(scope, body));
963
1012
  }
@@ -986,13 +1035,13 @@ function afterAll(hook) {
986
1035
  bunAfterAll(async () => hook(await sharedFixtures(scope)));
987
1036
  }
988
1037
  export {
989
- test,
990
- expect,
991
- describe,
992
- beforeEach,
993
- beforeAll,
994
- afterEach,
1038
+ E2eError2 as E2eError,
1039
+ WARDEN_E2E_VERSION2 as WARDEN_E2E_VERSION,
995
1040
  afterAll,
996
- WARDEN_E2E_VERSION,
997
- E2eError
1041
+ afterEach,
1042
+ beforeAll,
1043
+ beforeEach,
1044
+ describe,
1045
+ expect,
1046
+ test
998
1047
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delacour/warden-e2e",
3
- "version": "0.1.1",
3
+ "version": "0.3.1",
4
4
  "description": "TypeScript spec API for `warden e2e`: test, expect, screen, device, app",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -37,7 +37,7 @@
37
37
  "LICENSE"
38
38
  ],
39
39
  "peerDependencies": {
40
- "@delacour/warden": "0.1.1"
40
+ "@delacour/warden": "0.3.1"
41
41
  },
42
42
  "dependencies": {
43
43
  "zod": "^4.4.3"