-
Notifications
You must be signed in to change notification settings - Fork 17
Refactor CF E2E Tests to use Playwright APIs and replace custom promise approach #1285
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
Merged
LittorWired
merged 66 commits into
main
from
tl/cp-15822-use-waitForFunction-in-cf-e2e-tests
Aug 26, 2025
Merged
Changes from all commits
Commits
Show all changes
66 commits
Select commit
Hold shift + click to select a range
d89013b
export the CustomPage type
LittorWired 047474e
add _callObj to the global window type
LittorWired 754ba67
throw if call object not found
LittorWired 1902caa
refactor expectLayoutChanged to use expectToPass utility
LittorWired e0c8d77
expectToPass util wrapper
LittorWired 0017cd3
waitForFunction e2e util wrapper
LittorWired 792b477
refactor setLayoutOnPage to use waitForFunction util
LittorWired 9ad8353
Add unit tests for expectToPass utility in utils.spec.ts
LittorWired 6dfd8e6
refactor videoRoom.spec.ts to use expectToPass and waitForFunction ut…
LittorWired 36e8b3c
reassign top-level callSession and have sanity check to assert that v…
LittorWired acbe7a4
fix up old ts errors to return the proper page "type"
LittorWired 847ae6c
update DialAddress return type to allow generic
LittorWired 24d4fde
Add assertions to ensure SAT token, SignalWire, API token, and Relay …
LittorWired 3836329
revert throw
LittorWired a126d6d
cannot use expect inside of page.evaluate so throw instead
LittorWired 97a4b19
Added checks for callObj existence before usage. update ts-expect-err…
LittorWired b02eace
add return type
LittorWired abc8c44
throw if not call obj
LittorWired 2a709f2
do not poll for expectToPass and waitForFunction
LittorWired 8ae0931
add expectPageEvalToPass wrapper
LittorWired 725feab
update description and add custom assertion
LittorWired d18ff6d
refactor the videoRoom.spec.ts to use refactor expectPageEvalToPass p…
LittorWired 7433d9a
refactor the utils to use the expectPageEvalToPass pattern where appl…
LittorWired beb0335
PR feedback: use CallJoinedEventParams from signalwire/client
LittorWired bbe88b8
PR feedback: check expected properties for sanity check
LittorWired 249c2e8
PR feedback: remove the intercepWsTraffic package in fixtures
LittorWired 52bad3a
PR feedback remove playwrigth-ws-inspector @sw-internal/e2e-client
LittorWired 077c699
PR: feedback, remove unused try/catch block
LittorWired 58a0e79
Merge remote-tracking branch 'origin/main' into tl/cp-15822-use-waitF…
LittorWired 74c2a8a
PR feedback: revert the return of dialAddress type
LittorWired 4c16a8c
remove unused type
LittorWired 4e5af63
format
LittorWired 18e97b4
Refactor expectPageEvalToPass to accept an array for interval and ret…
LittorWired 19ce61a
PR feedback: refactor to take advantage of return value
LittorWired 8ed1139
remove unused util expectScreenShareJoined
LittorWired 6c853cf
refactor setLayoutOnPage to use expectPageEvalToPass util
LittorWired a5d5d13
mark TODO for utils that are not utilize for removal
LittorWired e166eed
PR feedback: expectToPass assertionMessage param to follow expect type
LittorWired a392b05
update existing utils utilize return value
LittorWired 9439667
PR feedback: provide better JSDocs comments for new utils
LittorWired 1b4a264
PR feedback: use line reporter
LittorWired 04cc22a
PR feedback: type assertion to force return type to avoid undefined c…
LittorWired e38eb42
PR feedback: revert dialAddress type return. Refactor dialAddress uti…
LittorWired 0999049
assign the return type from dialAddress
LittorWired b49465a
PR feedback: revert reporter changes
LittorWired f4b65d1
PR feedback: add TODO and addition tests for new util tests
LittorWired 1c82493
PR feedback: fix assertion message. make assert callback mandatory
LittorWired 3be9338
Refactor videoRoom.spec.ts to unify assertion messages and enforce ma…
LittorWired 208c959
fix lint error dialAddress utility to support generic return types
LittorWired 3f448af
Enhance assertion messages in videoRoom.spec.ts for clarity and consi…
LittorWired 68de21c
Enhance assertion messages in utils.ts for improved clarity and consi…
LittorWired 5a0c08a
fix the return type for dialAddress to default to any but allow generic
LittorWired ad1cc01
Refactor expectPageEvalToPass tests to unify assertion messages and e…
LittorWired f02774d
Refactor utils.spec.ts to enhance error message assertions and add ne…
LittorWired 2153dfd
PR feedback: refactor waitForFunction params to be consistent with ex…
LittorWired a39b9b9
Refactor waitForFunction calls in videoRoom.spec.ts to standardize pa…
LittorWired 9656599
remove only
LittorWired 912aa1d
increase timeout duration and assert result is undefined
LittorWired 76866bd
Update GitHub workflows to include 'default' project in the build mat…
LittorWired ee7f653
remove result assertion
LittorWired 6f834a3
Add timeout test for promise resolution and refine assertion messages…
LittorWired e5b51dc
Refactor error handling in expectPageEvalToPass test to ensure proper…
LittorWired 65402ee
remove only
LittorWired b9bee74
Update evaluateFn in expectPageEvalToPass test to return a rejected p…
LittorWired 0dfd2e5
remove only
LittorWired 94be26a
await the assertion promise
LittorWired File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,313 @@ | ||
import { expectPageEvalToPass, expectToPass, SERVER_URL } from '../../utils' | ||
import { test, expect } from '../../fixtures' | ||
|
||
test.describe('utils', () => { | ||
test.describe('expectToPass', () => { | ||
test('expectToPass: should resolve when the function passes', async ({ | ||
createCustomPage, | ||
}) => { | ||
const page = await createCustomPage({ name: '[page]' }) | ||
await page.goto(SERVER_URL) | ||
|
||
await expectToPass( | ||
async () => { | ||
const result = await page.waitForFunction(() => true) | ||
expect(await result.jsonValue()).toBe(true) | ||
}, | ||
{ message: 'should resolve when the function passes' } | ||
) | ||
}) | ||
|
||
test('should fail with a custom message and stack trace', async ({ | ||
createCustomPage, | ||
}) => { | ||
let expectedError: Error | undefined = undefined | ||
const page = await createCustomPage({ name: '[page]' }) | ||
await page.goto(SERVER_URL) | ||
|
||
try { | ||
await expectToPass( | ||
async () => { | ||
await page.waitForFunction(() => { | ||
throw new Error('test error') | ||
}) | ||
}, | ||
{ message: 'custom message' } | ||
) | ||
} catch (error) { | ||
expectedError = error | ||
} | ||
expect(expectedError).toBeDefined() | ||
expect(expectedError).toBeInstanceOf(Error) | ||
expect(expectedError).toMatchObject({ | ||
message: expect.stringContaining('custom message'), | ||
stack: expect.stringContaining('utils.spec.ts'), | ||
}) | ||
}) | ||
|
||
test('should timeout when waiting for a promise to resolve', async () => { | ||
expect( | ||
expectToPass( | ||
async () => { | ||
await new Promise((resolve) => setTimeout(resolve, 1000)) | ||
}, | ||
{ message: 'expect timeout' }, | ||
{ timeout: 100 } | ||
) | ||
).rejects.toThrow('Timeout 100ms exceeded while waiting on the predicate') | ||
}) | ||
|
||
test('should respect custom timeout option', async ({ | ||
createCustomPage, | ||
}) => { | ||
const page = await createCustomPage({ name: '[page]' }) | ||
|
||
const startTime = Date.now() | ||
let expectedError: Error | undefined = undefined | ||
|
||
try { | ||
await expectToPass( | ||
async () => { | ||
await page.waitForTimeout(3000) // Delay longer than timeout | ||
expect(false).toBe(true) // Will never pass | ||
}, | ||
{ message: 'should timeout in 1 second' }, | ||
{ timeout: 1000 } // Short timeout | ||
) | ||
} catch (error) { | ||
expectedError = error | ||
} | ||
|
||
const elapsedTime = Date.now() - startTime | ||
expect(expectedError).toBeDefined() | ||
expect(elapsedTime).toBeLessThan(2000) // Should timeout quickly | ||
}) | ||
|
||
test('should use custom interval array', async ({ createCustomPage }) => { | ||
const page = await createCustomPage({ name: '[page]' }) | ||
await page.goto(SERVER_URL) | ||
|
||
let attemptCount = 0 | ||
const attempts: number[] = [] | ||
|
||
await expectToPass( | ||
async () => { | ||
attempts.push(Date.now()) | ||
attemptCount++ | ||
if (attemptCount < 3) { | ||
throw new Error('Not ready yet') | ||
} | ||
// Pass on 3rd attempt | ||
}, | ||
{ message: 'should use custom intervals' }, | ||
{ interval: [100, 200, 300], timeout: 10000 } | ||
) | ||
|
||
expect(attemptCount).toBe(3) | ||
expect(attempts.length).toBe(3) | ||
}) | ||
|
||
test('should use default options when none provided', async () => { | ||
let attemptCount = 0 | ||
|
||
await expectToPass( | ||
async () => { | ||
attemptCount++ | ||
if (attemptCount < 2) { | ||
throw new Error('Not ready yet') | ||
} | ||
// Pass on 2nd attempt | ||
}, | ||
{ message: 'should use defaults' } | ||
// No options parameter | ||
) | ||
|
||
expect(attemptCount).toBe(2) | ||
}) | ||
|
||
test('should handle immediate success without retries', async () => { | ||
let attemptCount = 0 | ||
|
||
await expectToPass( | ||
async () => { | ||
attemptCount++ | ||
// Succeeds immediately | ||
expect(true).toBe(true) | ||
}, | ||
{ message: 'should succeed immediately' } | ||
) | ||
|
||
expect(attemptCount).toBe(1) // Should only run once | ||
}) | ||
|
||
test('should handle promise rejection properly', async () => { | ||
let expectedError: Error | undefined = undefined | ||
|
||
try { | ||
await expectToPass( | ||
async () => { | ||
return Promise.reject(new Error('Async rejection')) | ||
}, | ||
{ message: 'should handle rejection' }, | ||
{ timeout: 1000 } | ||
) | ||
} catch (error) { | ||
expectedError = error | ||
} | ||
|
||
expect(expectedError).toBeDefined() | ||
expect(expectedError?.message).toMatch(/should handle rejection/) | ||
}) | ||
|
||
test('should handle longer polling scenarios', async () => { | ||
let attemptCount = 0 | ||
const startTime = Date.now() | ||
|
||
await expectToPass( | ||
async () => { | ||
attemptCount++ | ||
// Simulate waiting for a condition that takes time | ||
if (Date.now() - startTime < 2000) { | ||
throw new Error('Still waiting...') | ||
} | ||
expect(attemptCount).toBeGreaterThan(1) | ||
}, | ||
{ message: 'should handle longer polling' }, | ||
{ timeout: 5000 } | ||
) | ||
|
||
expect(attemptCount).toBeGreaterThan(1) | ||
}) | ||
}) | ||
}) | ||
|
||
test.describe('waitForFunction', () => { | ||
test('TODO: should resolve when the function returns a truthy value', async () => { | ||
test.skip( | ||
true, | ||
'TODO: Implement test for waitForFunction resolving on truthy value' | ||
) | ||
}) | ||
|
||
test('TODO: should timeout if the function never returns truthy', async () => { | ||
test.skip(true, 'TODO: Implement test for waitForFunction timeout behavior') | ||
}) | ||
|
||
test('TODO: should pass arguments to the page function', async () => { | ||
test.skip(true, 'TODO: Implement test for waitForFunction argument passing') | ||
}) | ||
}) | ||
|
||
test.describe('expectPageEvalToPass', () => { | ||
test('should resolve when the page evaluation passes', async ({ | ||
createCustomPage, | ||
}) => { | ||
const page = await createCustomPage({ name: '[page]' }) | ||
|
||
const result = await expectPageEvalToPass(page, { | ||
assertionFn: (result) => { | ||
expect(result).toBe(true) | ||
}, | ||
evaluateFn: () => true, | ||
message: 'pass - resolve when the function returns a truthy value', | ||
}) | ||
|
||
expect(result).toBe(true) | ||
|
||
// with promise | ||
const result2 = await expectPageEvalToPass(page, { | ||
assertionFn: (result) => { | ||
expect(result).toBe(true) | ||
}, | ||
evaluateFn: () => Promise.resolve(true), | ||
message: 'pass - resolve when the function returns a truthy value', | ||
}) | ||
expect(result2).toBe(true) | ||
}) | ||
|
||
test('should throw when the evaluateFn throws an error', async ({ | ||
createCustomPage, | ||
}) => { | ||
const page = await createCustomPage({ name: '[page]' }) | ||
|
||
await expect( | ||
expectPageEvalToPass(page, { | ||
assertionFn: (result: unknown) => { | ||
// should not be called because the evaluateFn throws an error | ||
expect(result).not.toBeInstanceOf(Error) | ||
expect(result).not.toMatchObject({ | ||
message: 'test error', | ||
}) | ||
// should never pass to ensure expect().toPass() does not resolve | ||
expect(false).toBe(true) | ||
}, | ||
evaluateFn: () => { | ||
return new Promise((_resolve, reject) => { | ||
setTimeout(() => { | ||
reject(new Error('test error')) | ||
}, 100) | ||
}) | ||
}, | ||
message: 'expect error', | ||
}) | ||
).rejects.toThrow('expect error') | ||
}) | ||
|
||
test('should pass evaluateArgs to the evaluateFn and return the serializable object', async ({ | ||
createCustomPage, | ||
}) => { | ||
const page = await createCustomPage({ name: '[page]' }) | ||
|
||
const result = await expectPageEvalToPass(page, { | ||
assertionFn: (result) => { | ||
expect(result).toMatchObject({ | ||
param: 'test', | ||
param2: false, | ||
param3: 123, | ||
param4: {}, | ||
}) | ||
}, | ||
evaluateArgs: { | ||
param: 'test', | ||
param2: false, | ||
param3: 123, | ||
param4: {}, | ||
}, | ||
evaluateFn: (params) => { | ||
return params | ||
}, | ||
message: 'pass - resolve when the function returns a truthy value', | ||
}) | ||
|
||
expect(result).toMatchObject({ | ||
param: 'test', | ||
param2: false, | ||
param3: 123, | ||
param4: {}, | ||
}) | ||
}) | ||
|
||
test('should timeout when page evaluation takes too long', async ({ | ||
createCustomPage, | ||
}) => { | ||
const page = await createCustomPage({ name: '[page]' }) | ||
|
||
await expect( | ||
expectPageEvalToPass(page, { | ||
assertionFn: (result) => { | ||
// should never be called | ||
expect(result).not.toMatch('should not resolve') | ||
}, | ||
evaluateFn: () => { | ||
return new Promise((resolve) => | ||
setTimeout(() => resolve('should not resolve'), 5000) | ||
) | ||
}, | ||
message: 'timeout - should timeout when page evaluation takes too long', | ||
timeoutMs: 100, | ||
}) | ||
).rejects.toThrow( | ||
'timeout - should timeout when page evaluation takes too long' | ||
) | ||
}) | ||
}) |
Oops, something went wrong.
Oops, something went wrong.
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.
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.
Highly appreciate all these utility tests.
Should we add TODO or tests for the other new utilities such as
waitForFunction
orexpectPageEvalToPass
?