-
Notifications
You must be signed in to change notification settings - Fork 0
feat: Implemented playwright-reporter and helper #33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1 +1,91 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Detects current test framework runtime | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type Framework = 'playwright' | 'jest' | 'vitest' | 'unknown'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function detectFramework(): Framework { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const env = process.env; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (env['TEST_WORKER_INDEX'] !== undefined) return 'playwright'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (env['JEST_WORKER_ID'] !== undefined) return 'jest'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (env['VITEST'] !== undefined) return 'vitest'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return 'unknown'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interface ContextAdapter { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| set(type: string, value: string): void; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Pushes metadata into test.info().annotations. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * The reporter reads these back via test.annotations in onTestEnd. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class PlaywrightContext implements ContextAdapter { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| set(type: string, value: string): void { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // eslint-disable-next-line @typescript-eslint/no-require-imports | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { test } = require('@playwright/test'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const info = test.info(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (info) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| info.annotations.push({ type, description: value }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Not inside a Playwright worker — silently ignore. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * For runners without per-test annotation APIs, we use a module-level Map. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Key = current test name (from expect.getState()), value = annotations. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const globalStore = new Map< | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| string, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Array<{ type: string; description: string }> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| >(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class GlobalStoreContext implements ContextAdapter { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| set(type: string, value: string): void { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const testName = this.currentTestName() ?? '__default__'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let entries = globalStore.get(testName); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!entries) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| entries = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| globalStore.set(testName, entries); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| entries.push({ type, description: value }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private currentTestName(): string | undefined { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // eslint-disable-next-line @typescript-eslint/no-require-imports | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { expect } = require('expect'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const state = expect.getState() as { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| currentTestName?: string; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+56
to
+60
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // eslint-disable-next-line @typescript-eslint/no-require-imports | |
| const { expect } = require('expect'); | |
| const state = expect.getState() as { | |
| currentTestName?: string; | |
| }; | |
| type ExpectLike = { | |
| getState(): { | |
| currentTestName?: string; | |
| }; | |
| }; | |
| const globalExpect = ( | |
| globalThis as typeof globalThis & { expect?: ExpectLike } | |
| ).expect; | |
| const expectInstance = | |
| globalExpect ?? | |
| (() => { | |
| // eslint-disable-next-line @typescript-eslint/no-require-imports | |
| const { expect } = require('expect'); | |
| return expect as ExpectLike | undefined; | |
| })(); | |
| if (!expectInstance || typeof expectInstance.getState !== 'function') { | |
| return undefined; | |
| } | |
| const state = expectInstance.getState(); |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1 +1,62 @@ | ||||||||||||||||||||||||||||
| // Exposes collected metadata to reporters | ||||||||||||||||||||||||||||
| import type { AssertiveMetadata } from './types'; | ||||||||||||||||||||||||||||
| import { drainGlobalStore } from './context'; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+3
|
||||||||||||||||||||||||||||
| import type { AssertiveMetadata } from './types'; | |
| import { drainGlobalStore } from './context'; | |
| import { drainGlobalStore } from './context'; | |
| export type AssertiveMetadata = { | |
| testId: string | undefined; | |
| tags: string[]; | |
| owner: string | undefined; | |
| priority: string | undefined; | |
| testType: string | undefined; | |
| customFields: Record<string, string>; | |
| attachments: Record<string, string>; | |
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,59 @@ | ||
| // assertive.id(), .tags(), .owner() | ||
| import type { Priority, TestType } from './types'; | ||
| import { getContext } from './context'; | ||
|
Comment on lines
+1
to
+2
|
||
|
|
||
| /** | ||
| * Universal helper client — import this in test files. | ||
| * | ||
| * import { assertive } from 'getassertive/helper'; | ||
| * | ||
| * test('User can checkout', async ({ page }) => { | ||
| * assertive.id('TST-123'); | ||
| * assertive.tags('checkout', 'smoke'); | ||
| * assertive.owner('aayush'); | ||
| * assertive.priority('high'); | ||
| * // … test code … | ||
| * }); | ||
| */ | ||
| class AssertiveHelper { | ||
| /** Link this test to a unique assertive ID (e.g. "TST-123"). */ | ||
| id(testId: string): void { | ||
| getContext().set('assertive_id', testId); | ||
| } | ||
|
|
||
| /** Attach one or more tags to the current test. */ | ||
| tags(...tags: string[]): void { | ||
| for (const tag of tags) { | ||
| getContext().set('assertive_tag', tag); | ||
| } | ||
| } | ||
|
|
||
| /** Set the owner / assignee for the current test. */ | ||
| owner(name: string): void { | ||
| getContext().set('assertive_owner', name); | ||
| } | ||
|
|
||
| /** Set the priority level — full autocomplete for the four levels. */ | ||
| priority(level: Priority): void { | ||
| getContext().set('assertive_priority', level); | ||
| } | ||
|
|
||
| /** Set the test type. */ | ||
| type(testType: TestType): void { | ||
| getContext().set('assertive_type', testType); | ||
| } | ||
|
|
||
| /** Attach an arbitrary custom field. */ | ||
| field(key: string, value: string): void { | ||
| getContext().set(`assertive_field_${key}`, value); | ||
| } | ||
|
|
||
| /** Attach contextual data (e.g. a cart total, a screenshot path). */ | ||
| attach(key: string, data: string): void { | ||
| getContext().set(`assertive_attach_${key}`, data); | ||
| } | ||
| } | ||
|
|
||
| /** Singleton — import this in test files. */ | ||
| export const assertive = new AssertiveHelper(); | ||
|
|
||
| export type { Priority, TestType }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@playwright/testis required at runtime by the published reporter (import ... from '@playwright/test/reporter'), but it’s added underdevDependencies, which won’t be installed for consumers. Move it topeerDependencies(optionallypeerDependenciesMeta.optional) ordependenciesso the reporter can be required in downstream projects.