Skip to content

Commit ff63f70

Browse files
authored
fix: sessions playwright traces - naming convention and error handling (#5073)
1 parent 4daabc8 commit ff63f70

File tree

3 files changed

+197
-9
lines changed

3 files changed

+197
-9
lines changed

lib/helper/Playwright.js

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2377,15 +2377,19 @@ class Playwright extends Helper {
23772377
if (this.options.recordVideo && this.page && this.page.video()) {
23782378
test.artifacts.video = saveVideoForPage(this.page, `${test.title}.failed`)
23792379
for (const sessionName in this.sessionPages) {
2380-
test.artifacts[`video_${sessionName}`] = saveVideoForPage(this.sessionPages[sessionName], `${test.title}_${sessionName}.failed`)
2380+
if (sessionName === '') continue
2381+
test.artifacts[`video_${sessionName}`] = saveVideoForPage(this.sessionPages[sessionName], `${sessionName}_${test.title}.failed`)
23812382
}
23822383
}
23832384

23842385
if (this.options.trace) {
23852386
test.artifacts.trace = await saveTraceForContext(this.browserContext, `${test.title}.failed`)
23862387
for (const sessionName in this.sessionPages) {
2387-
if (!this.sessionPages[sessionName].context) continue
2388-
test.artifacts[`trace_${sessionName}`] = await saveTraceForContext(this.sessionPages[sessionName].context, `${test.title}_${sessionName}.failed`)
2388+
if (sessionName === '') continue
2389+
const sessionPage = this.sessionPages[sessionName]
2390+
const sessionContext = sessionPage.context()
2391+
if (!sessionContext || !sessionContext.tracing) continue
2392+
test.artifacts[`trace_${sessionName}`] = await saveTraceForContext(sessionContext, `${sessionName}_${test.title}.failed`)
23892393
}
23902394
}
23912395

@@ -2399,7 +2403,8 @@ class Playwright extends Helper {
23992403
if (this.options.keepVideoForPassedTests) {
24002404
test.artifacts.video = saveVideoForPage(this.page, `${test.title}.passed`)
24012405
for (const sessionName of Object.keys(this.sessionPages)) {
2402-
test.artifacts[`video_${sessionName}`] = saveVideoForPage(this.sessionPages[sessionName], `${test.title}_${sessionName}.passed`)
2406+
if (sessionName === '') continue
2407+
test.artifacts[`video_${sessionName}`] = saveVideoForPage(this.sessionPages[sessionName], `${sessionName}_${test.title}.passed`)
24032408
}
24042409
} else {
24052410
this.page
@@ -2414,8 +2419,11 @@ class Playwright extends Helper {
24142419
if (this.options.trace) {
24152420
test.artifacts.trace = await saveTraceForContext(this.browserContext, `${test.title}.passed`)
24162421
for (const sessionName in this.sessionPages) {
2417-
if (!this.sessionPages[sessionName].context) continue
2418-
test.artifacts[`trace_${sessionName}`] = await saveTraceForContext(this.sessionPages[sessionName].context, `${test.title}_${sessionName}.passed`)
2422+
if (sessionName === '') continue
2423+
const sessionPage = this.sessionPages[sessionName]
2424+
const sessionContext = sessionPage.context()
2425+
if (!sessionContext || !sessionContext.tracing) continue
2426+
test.artifacts[`trace_${sessionName}`] = await saveTraceForContext(sessionContext, `${sessionName}_${test.title}.passed`)
24192427
}
24202428
}
24212429
} else {
@@ -3883,9 +3891,18 @@ function saveVideoForPage(page, name) {
38833891
async function saveTraceForContext(context, name) {
38843892
if (!context) return
38853893
if (!context.tracing) return
3886-
const fileName = `${`${global.output_dir}${pathSeparator}trace${pathSeparator}${uuidv4()}_${clearString(name)}`.slice(0, 245)}.zip`
3887-
await context.tracing.stop({ path: fileName })
3888-
return fileName
3894+
try {
3895+
const fileName = `${`${global.output_dir}${pathSeparator}trace${pathSeparator}${uuidv4()}_${clearString(name)}`.slice(0, 245)}.zip`
3896+
await context.tracing.stop({ path: fileName })
3897+
return fileName
3898+
} catch (err) {
3899+
// Handle the case where tracing was not started or context is invalid
3900+
if (err.message && err.message.includes('Must start tracing before stopping')) {
3901+
// Tracing was never started on this context, silently skip
3902+
return null
3903+
}
3904+
throw err
3905+
}
38893906
}
38903907

38913908
async function highlightActiveElement(element) {

test/helper/Playwright_test.js

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1489,6 +1489,172 @@ describe('Playwright - Video & Trace & HAR', () => {
14891489
expect(test.artifacts.trace).to.include(path.join(global.output_dir, 'trace'))
14901490
expect(test.artifacts.har).to.include(path.join(global.output_dir, 'har'))
14911491
})
1492+
1493+
it('checks that video and trace are recorded for sessions', async () => {
1494+
// Reset test artifacts
1495+
test.artifacts = {}
1496+
1497+
await I.amOnPage('about:blank')
1498+
await I.executeScript(() => (document.title = 'Main Session'))
1499+
1500+
// Create a session and perform actions
1501+
const session = I._session()
1502+
const sessionName = 'test_session'
1503+
I.activeSessionName = sessionName
1504+
1505+
// Start session and get context
1506+
const sessionContext = await session.start(sessionName, {})
1507+
I.sessionPages[sessionName] = (await sessionContext.pages())[0]
1508+
1509+
// Simulate session actions
1510+
await I.sessionPages[sessionName].goto('about:blank')
1511+
await I.sessionPages[sessionName].evaluate(() => (document.title = 'Session Test'))
1512+
1513+
// Trigger failure to save artifacts
1514+
await I._failed(test)
1515+
1516+
// Check main session artifacts
1517+
assert(test.artifacts)
1518+
expect(Object.keys(test.artifacts)).to.include('trace')
1519+
expect(Object.keys(test.artifacts)).to.include('video')
1520+
1521+
// Check session-specific artifacts with correct naming convention
1522+
const sessionVideoKey = `video_${sessionName}`
1523+
const sessionTraceKey = `trace_${sessionName}`
1524+
1525+
expect(Object.keys(test.artifacts)).to.include(sessionVideoKey)
1526+
expect(Object.keys(test.artifacts)).to.include(sessionTraceKey)
1527+
1528+
// Verify file naming convention: session name comes first
1529+
// The file names should contain the session name at the beginning
1530+
expect(test.artifacts[sessionVideoKey]).to.include(sessionName)
1531+
expect(test.artifacts[sessionTraceKey]).to.include(sessionName)
1532+
1533+
// Cleanup
1534+
await sessionContext.close()
1535+
delete I.sessionPages[sessionName]
1536+
})
1537+
1538+
it('handles sessions with long test titles correctly', async () => {
1539+
// Create a test with a very long title to test truncation behavior
1540+
const longTest = {
1541+
title:
1542+
'this_is_a_very_long_test_title_that_would_cause_issues_with_file_naming_when_session_names_are_appended_at_the_end_instead_of_the_beginning_which_could_lead_to_collisions_between_different_sessions_writing_to_the_same_file_path_due_to_truncation',
1543+
artifacts: {},
1544+
}
1545+
1546+
await I.amOnPage('about:blank')
1547+
1548+
// Create multiple sessions with different names
1549+
const session1 = I._session()
1550+
const session2 = I._session()
1551+
const sessionName1 = 'session_one'
1552+
const sessionName2 = 'session_two'
1553+
1554+
I.activeSessionName = sessionName1
1555+
const sessionContext1 = await session1.start(sessionName1, {})
1556+
I.sessionPages[sessionName1] = (await sessionContext1.pages())[0]
1557+
1558+
I.activeSessionName = sessionName2
1559+
const sessionContext2 = await session2.start(sessionName2, {})
1560+
I.sessionPages[sessionName2] = (await sessionContext2.pages())[0]
1561+
1562+
// Trigger failure to save artifacts
1563+
await I._failed(longTest)
1564+
1565+
// Check that different sessions have different file paths
1566+
const session1VideoKey = `video_${sessionName1}`
1567+
const session2VideoKey = `video_${sessionName2}`
1568+
const session1TraceKey = `trace_${sessionName1}`
1569+
const session2TraceKey = `trace_${sessionName2}`
1570+
1571+
expect(longTest.artifacts[session1VideoKey]).to.not.equal(longTest.artifacts[session2VideoKey])
1572+
expect(longTest.artifacts[session1TraceKey]).to.not.equal(longTest.artifacts[session2TraceKey])
1573+
1574+
// Verify that session names are present in filenames (indicating the naming fix works)
1575+
expect(longTest.artifacts[session1VideoKey]).to.include(sessionName1)
1576+
expect(longTest.artifacts[session2VideoKey]).to.include(sessionName2)
1577+
expect(longTest.artifacts[session1TraceKey]).to.include(sessionName1)
1578+
expect(longTest.artifacts[session2TraceKey]).to.include(sessionName2)
1579+
1580+
// Cleanup
1581+
await sessionContext1.close()
1582+
await sessionContext2.close()
1583+
delete I.sessionPages[sessionName1]
1584+
delete I.sessionPages[sessionName2]
1585+
})
1586+
1587+
it('skips main session in session artifacts processing', async () => {
1588+
// Reset test artifacts
1589+
test.artifacts = {}
1590+
1591+
await I.amOnPage('about:blank')
1592+
1593+
// Simulate having a main session (empty string name) in sessionPages
1594+
I.sessionPages[''] = I.page
1595+
1596+
// Create a regular session
1597+
const session = I._session()
1598+
const sessionName = 'regular_session'
1599+
I.activeSessionName = sessionName
1600+
const sessionContext = await session.start(sessionName, {})
1601+
I.sessionPages[sessionName] = (await sessionContext.pages())[0]
1602+
1603+
// Trigger failure to save artifacts
1604+
await I._failed(test)
1605+
1606+
// Check that main session artifacts are present (not duplicated)
1607+
expect(Object.keys(test.artifacts)).to.include('trace')
1608+
expect(Object.keys(test.artifacts)).to.include('video')
1609+
1610+
// Check that regular session artifacts are present
1611+
expect(Object.keys(test.artifacts)).to.include(`video_${sessionName}`)
1612+
expect(Object.keys(test.artifacts)).to.include(`trace_${sessionName}`)
1613+
1614+
// Check that there are no duplicate main session artifacts with empty key
1615+
expect(Object.keys(test.artifacts)).to.not.include('video_')
1616+
expect(Object.keys(test.artifacts)).to.not.include('trace_')
1617+
1618+
// Cleanup
1619+
await sessionContext.close()
1620+
delete I.sessionPages[sessionName]
1621+
delete I.sessionPages['']
1622+
})
1623+
1624+
it('gracefully handles tracing errors for invalid session contexts', async () => {
1625+
// Reset test artifacts
1626+
test.artifacts = {}
1627+
1628+
await I.amOnPage('about:blank')
1629+
1630+
// Create a real session that we can manipulate
1631+
const session = I._session()
1632+
const sessionName = 'error_session'
1633+
I.activeSessionName = sessionName
1634+
const sessionContext = await session.start(sessionName, {})
1635+
I.sessionPages[sessionName] = (await sessionContext.pages())[0]
1636+
1637+
// Manually stop tracing to create the error condition
1638+
try {
1639+
await sessionContext.tracing.stop()
1640+
} catch (e) {
1641+
// This may fail if tracing wasn't started, which is fine
1642+
}
1643+
1644+
// Now when _failed is called, saveTraceForContext should handle the tracing error gracefully
1645+
await I._failed(test)
1646+
1647+
// Main artifacts should still be created
1648+
expect(Object.keys(test.artifacts)).to.include('trace')
1649+
expect(Object.keys(test.artifacts)).to.include('video')
1650+
1651+
// Session video should still be created despite tracing error
1652+
expect(Object.keys(test.artifacts)).to.include(`video_${sessionName}`)
1653+
1654+
// Cleanup
1655+
await sessionContext.close()
1656+
delete I.sessionPages[sessionName]
1657+
})
14921658
})
14931659
describe('Playwright - HAR', () => {
14941660
before(() => {

test/unit/plugin/screenshotOnFail_test.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,18 @@ describe('screenshotOnFail', () => {
124124
screenshotOnFail({ uniqueScreenshotNames: true })
125125
const test = createTest('test1')
126126

127+
// Use sinon to stub Date.now to return consistent timestamp
128+
const clock = sinon.useFakeTimers(1755596785000) // Fixed timestamp
129+
127130
const helper = new MochawesomeHelper({ uniqueScreenshotNames: true })
128131
const spy = sinon.spy(helper, '_addContext')
129132
helper._failed(test)
130133

131134
event.dispatcher.emit(event.test.failed, test)
132135
await recorder.promise()
133136

137+
clock.restore()
138+
134139
const screenshotFileName = screenshotSaved.getCall(0).args[0]
135140
expect(spy.getCall(0).args[1]).to.equal(screenshotFileName)
136141
})

0 commit comments

Comments
 (0)