@serenity-js/playwright-test 3.36.2 → 3.37.0

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.
@@ -13,7 +13,7 @@ import type {
13
13
  TestType,
14
14
  WorkerInfo,
15
15
  } from '@playwright/test';
16
- import { test as playwrightBaseTest } from '@playwright/test';
16
+ import { mergeTests, test as playwrightBaseTest } from '@playwright/test';
17
17
  import type { DiffFormatter } from '@serenity-js/core';
18
18
  import { AnsiDiffFormatter, Cast, Clock, Duration, Serenity, TakeNotes } from '@serenity-js/core';
19
19
  import { SceneFinishes, SceneTagged } from '@serenity-js/core/lib/events';
@@ -68,20 +68,32 @@ export const fixtures: Fixtures<SerenityFixtures & SerenityInternalFixtures, Ser
68
68
  { option: true },
69
69
  ],
70
70
 
71
+ axios: async ({ baseURL, extraHTTPHeaders, proxy }, use) => {
72
+ await use({
73
+ baseURL: baseURL,
74
+ headers: extraHTTPHeaders,
75
+ proxy: proxy && proxy?.server
76
+ ? asProxyConfig(proxy)
77
+ : undefined,
78
+ })
79
+ },
80
+
71
81
  actors: [
72
82
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
73
- async ({ extraContextOptions, baseURL, extraHTTPHeaders, page, proxy }, use): Promise<void> => {
74
- await use(Cast.where(actor => actor.whoCan(
75
- BrowseTheWebWithPlaywright.usingPage(page, extraContextOptions),
76
- TakeNotes.usingAnEmptyNotepad(),
77
- CallAnApi.using({
78
- baseURL: baseURL,
79
- headers: extraHTTPHeaders,
80
- proxy: proxy && proxy?.server
81
- ? asProxyConfig(proxy)
82
- : undefined,
83
- }),
84
- )));
83
+ async ({ axios, extraAbilities, extraContextOptions, page }, use): Promise<void> => {
84
+ await use(Cast.where(actor => {
85
+
86
+ const abilities = Array.isArray(extraAbilities)
87
+ ? extraAbilities
88
+ : extraAbilities(actor.name);
89
+
90
+ return actor.whoCan(
91
+ BrowseTheWebWithPlaywright.usingPage(page, extraContextOptions),
92
+ TakeNotes.usingAnEmptyNotepad(),
93
+ CallAnApi.using(axios),
94
+ ...abilities,
95
+ );
96
+ }));
85
97
  },
86
98
  { option: true },
87
99
  ],
@@ -109,7 +121,7 @@ export const fixtures: Fixtures<SerenityFixtures & SerenityInternalFixtures, Ser
109
121
 
110
122
  sceneIdFactoryInternal: [
111
123
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types,no-empty-pattern
112
- async ({ }, use) => {
124
+ async ({}, use) => {
113
125
  await use(new PlaywrightTestSceneIdFactory());
114
126
  },
115
127
  { scope: 'worker', box: true },
@@ -132,7 +144,7 @@ export const fixtures: Fixtures<SerenityFixtures & SerenityInternalFixtures, Ser
132
144
 
133
145
  eventStreamWriterInternal: [
134
146
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types,no-empty-pattern
135
- async ({ }, use, workerInfo) => {
147
+ async ({}, use, workerInfo) => {
136
148
 
137
149
  const serenityOutputDirectory = path.join(workerInfo.project.outputDir, 'serenity');
138
150
 
@@ -225,12 +237,18 @@ export const fixtures: Fixtures<SerenityFixtures & SerenityInternalFixtures, Ser
225
237
  { auto: true, box: true, }
226
238
  ],
227
239
 
240
+ extraAbilities: [
241
+ [],
242
+ { option: true },
243
+ ],
244
+
228
245
  actorCalled: [
229
246
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
230
247
  async ({ serenity }, use) => {
231
248
 
232
249
  const actorCalled = (name: string) => {
233
250
  const actor = serenity.theActorCalled(name);
251
+
234
252
  return actor.whoCan(new PerformActivitiesAsPlaywrightSteps(actor, serenity, it));
235
253
  };
236
254
 
@@ -247,7 +265,7 @@ export const fixtures: Fixtures<SerenityFixtures & SerenityInternalFixtures, Ser
247
265
  /**
248
266
  * Serenity/JS BDD-style test API created by [`useBase`](https://serenity-js.org/api/playwright-test/function/useBase/).
249
267
  */
250
- export type TestApi<TestArgs extends Record<string, any>, WorkerArgs extends Record<string, any>> =
268
+ export type TestApi<TestArgs extends object, WorkerArgs extends object> =
251
269
  Pick<TestType<TestArgs, WorkerArgs>, 'describe' | 'beforeAll' | 'beforeEach' | 'afterEach' | 'afterAll' | 'expect'> &
252
270
  {
253
271
  /**
@@ -274,7 +292,7 @@ export type TestApi<TestArgs extends Record<string, any>, WorkerArgs extends Rec
274
292
  *
275
293
  * Shorthand for [`useBase`](https://serenity-js.org/api/playwright-test/function/useBase/)
276
294
  */
277
- useFixtures: <T extends Record<string, any>, W extends Record<string, any> = object>(
295
+ useFixtures: <T extends object, W extends object = object>(
278
296
  customFixtures: Fixtures<T, W, TestArgs, WorkerArgs>
279
297
  ) => TestApi<TestArgs & T, WorkerArgs & W>,
280
298
 
@@ -282,7 +300,7 @@ export type TestApi<TestArgs extends Record<string, any>, WorkerArgs extends Rec
282
300
  test: TestType<TestArgs, WorkerArgs>,
283
301
  }
284
302
 
285
- function createTestApi<BaseTestFixtures extends (PlaywrightTestArgs & PlaywrightTestOptions), BaseWorkerFixtures extends (PlaywrightWorkerArgs & PlaywrightWorkerOptions)>(baseTest: TestType<BaseTestFixtures, BaseWorkerFixtures>): TestApi<BaseTestFixtures, BaseWorkerFixtures> {
303
+ function createTestApi<BaseTestFixtures extends object, BaseWorkerFixtures extends object>(baseTest: TestType<BaseTestFixtures, BaseWorkerFixtures>): TestApi<BaseTestFixtures, BaseWorkerFixtures> {
286
304
  return {
287
305
  useFixtures<T extends Record<string, any>, W extends Record<string, any> = object>(customFixtures: Fixtures<T, W, BaseTestFixtures, BaseWorkerFixtures>): TestApi<BaseTestFixtures & T, BaseWorkerFixtures & W> {
288
306
  return createTestApi(baseTest.extend(customFixtures));
@@ -426,6 +444,9 @@ export const expect: Expect = api.expect;
426
444
 
427
445
  export const useFixtures = api.useFixtures;
428
446
 
447
+ type MergedT<List> = List extends [ TestType<infer T, any>, ...(infer Rest) ] ? T & MergedT<Rest> : object;
448
+ type MergedW<List> = List extends [ TestType<any, infer W>, ...(infer Rest) ] ? W & MergedW<Rest> : object;
449
+
429
450
  /**
430
451
  * Creates a Serenity/JS BDD-style test API around the given Playwright [base test](https://playwright.dev/docs/test-fixtures).
431
452
  *
@@ -562,15 +583,45 @@ export const useFixtures = api.useFixtures;
562
583
  * })
563
584
  * ```
564
585
  *
565
- * @param baseTest
586
+ * ## Merging multiple base tests
587
+ *
588
+ * To merge fixtures from multiple files or modules, pass them to `useBase`.
589
+ *
590
+ * ```tsx
591
+ * import { test as componentTest } from '@playwright/experimental-ct-react'
592
+ * import { test as a11yTest } from 'my-a11y-test-utils';
593
+ * import { Ensure, contain } from '@serenity-js/assertions'
594
+ * import { useBase } from '@serenity-js/playwright-test'
595
+ * import { Enter, PageElement, CssClasses } from '@serenity-js/web'
596
+ *
597
+ * import EmailInput from './EmailInput';
598
+ *
599
+ * const { it, describe } = useBase(componentTest, a11yTest).useFixtures<{ emailAddress: string }>({
600
+ * emailAddress: ({ actor }, use) => {
601
+ * use(`${ actor.name }@example.org`)
602
+ * }
603
+ * })
604
+ *
605
+ * describe('EmailInput', () => {
606
+ *
607
+ * it('allows valid email addresses', async ({ actor, mount, emailAddress }) => {
608
+ * const nativeComponent = await mount(<EmailInput/>);
609
+ *
610
+ * const component = PageElement.from(nativeComponent);
611
+ *
612
+ * await actor.attemptsTo(
613
+ * Enter.theValue(emailAddress).into(component),
614
+ * Ensure.that(CssClasses.of(component), contain('valid')),
615
+ * )
616
+ * })
617
+ * })
618
+ * ```
619
+ *
620
+ * @param baseTests
566
621
  */
567
- export function useBase<
568
- BaseTestFixtures extends (PlaywrightTestArgs & PlaywrightTestOptions),
569
- BaseWorkerFixtures extends (PlaywrightWorkerArgs & PlaywrightWorkerOptions)
570
- > (baseTest: TestType<BaseTestFixtures, BaseWorkerFixtures>): TestApi<BaseTestFixtures & SerenityFixtures, BaseWorkerFixtures & SerenityWorkerFixtures> {
571
- return createTestApi<BaseTestFixtures, BaseWorkerFixtures>(baseTest).useFixtures(
572
- fixtures as Fixtures<SerenityFixtures, SerenityWorkerFixtures, BaseTestFixtures, BaseWorkerFixtures>
573
- );
622
+ export function useBase<List extends any[]>(...baseTests: List): TestApi<MergedT<List> & SerenityFixtures, MergedW<List> & SerenityWorkerFixtures> {
623
+ return createTestApi<MergedT<List>, MergedW<List>>(mergeTests(...baseTests))
624
+ .useFixtures(fixtures as Fixtures<SerenityFixtures, SerenityWorkerFixtures, MergedT<List>, MergedW<List>>);
574
625
  }
575
626
 
576
627
  /**
@@ -602,13 +653,14 @@ function asProxyConfig(proxy: PlaywrightTestOptions['proxy']): {
602
653
  auth?: { username: string, password: string }
603
654
  bypass?: string;
604
655
  } {
656
+ const proxyServer = proxy.server.trim();
605
657
 
606
658
  // Playwright defaults to http when proxy.server does not define the protocol
607
659
  // See https://playwright.dev/docs/api/class-testoptions#test-options-proxy
608
- const hasProtocol = /[\dA-Za-z]+:\/\//.test(proxy.server);
660
+ const hasProtocol = /^[\dA-Za-z]+:\/\//.test(proxyServer);
609
661
  const proxyUrl = hasProtocol
610
- ? new URL(proxy.server)
611
- : new URL(`http://${ proxy.server }`);
662
+ ? new URL(proxyServer)
663
+ : new URL(`http://${ proxyServer }`);
612
664
 
613
665
  const host = proxyUrl.hostname;
614
666
  const port = proxyUrl.port