11import { expect } from '@playwright/test'
22
3- // Return a valid URL of the test course or account - assertVariables.js will
4- // ensure these env vars exist and are normalised.
5- export const TEST_URL = process . env . CANVAS_HOST + "/" + process . env . TEST_PATH
3+ /**
4+ * Normalised test URL built from `CANVAS_HOST` and `TEST_PATH`.
5+ * `src/setup/assertVariables.js` ensures these env vars exist and are normalised.
6+ * @type {string }
7+ */
8+ export const TEST_URL = process . env . CANVAS_HOST + '/' + process . env . TEST_PATH
69
710
11+ /**
12+ * Perform an LTI login by requesting a session token and navigating the page
13+ * to the returned session URL.
14+ * @param {import('@playwright/test').APIRequestContext } request - Playwright request context
15+ * @param {import('@playwright/test').Page } page - Playwright page to navigate
16+ * @param {string } host - Canvas host (base URL)
17+ * @param {string } token - OAuth bearer token
18+ * @returns {Promise<void> }
19+ */
820export const login = async ( request , page , host , token ) => {
921 await Promise . resolve (
1022 await request . get ( `${ host } /login/session_token` , {
@@ -23,7 +35,19 @@ export const login = async (request, page, host, token) => {
2335 )
2436}
2537
26- export const grantAccessIfNeeded = async ( page , context , toolUrl ) => {
38+
39+ /**
40+ * Visit `toolUrl` and complete the grant-access flow if the tool requests it.
41+ * Navigates the page into the LTI tool, waits for loading to finish and
42+ * resolves whether the tool requires an explicit grant. If required, the
43+ * `grantAccess` helper is used to complete the flow.
44+ *
45+ * @param {import('@playwright/test').Page } page - Playwright page instance
46+ * @param {import('@playwright/test').BrowserContext } context - Playwright browser context
47+ * @param {string } toolUrl - URL of the LTI tool to visit
48+ * @returns {Promise<void> }
49+ */
50+ export const grantAccessIfNeeded = async ( page , context , toolUrl ) => {
2751 await page . goto ( toolUrl )
2852 const ltiToolFrame = getLtiIFrame ( page )
2953
@@ -36,49 +60,85 @@ export const grantAccessIfNeeded = async(page, context, toolUrl) => {
3660
3761 const needsGrantAccess = await Promise . race ( [
3862 ltiToolFrame . getByText ( 'Please Grant Access' ) . waitFor ( )
39- . then ( ( ) => { return true } ) ,
63+ . then ( ( ) => { return true } ) ,
4064 waitForNoSpinners ( ltiToolFrame , 3000 )
41- . then ( ( ) => { return false } )
65+ . then ( ( ) => { return false } )
4266 ] )
4367
44- if ( needsGrantAccess ) {
68+ if ( needsGrantAccess ) {
4569 await grantAccess ( context , ltiToolFrame )
4670 }
4771}
4872
73+ /**
74+ * Complete the grant access flow by clicking the authorise button in the
75+ * popup page. Intended to be used by `grantAccessIfNeeded`.
76+ *
77+ * @param {import('@playwright/test').BrowserContext } context - Playwright browser context
78+ * @param {import('@playwright/test').FrameLocator } frameLocator - Locator for the LTI frame
79+ * @returns {Promise<void> }
80+ */
4981const grantAccess = async ( context , frameLocator ) => {
5082 const button = await frameLocator . getByRole ( 'button' )
5183 const [ newPage ] = await Promise . all ( [
5284 context . waitForEvent ( 'page' ) ,
5385 button . click ( )
5486 ] )
5587
56- const submit = await newPage . getByRole ( 'button' , { name : / A u t h o r i [ s z ] e / } )
88+ const submit = await newPage . getByRole ( 'button' , { name : / A u t h o r i [ s z ] e / } )
5789 await submit . click ( )
58- const close = await newPage . getByText ( 'Close' , { exact : true } )
90+ const close = await newPage . getByText ( 'Close' , { exact : true } )
5991 await close . click ( )
6092}
6193
94+
95+ /**
96+ * Return the frame locator for the LTI launch iframe.
97+ * @param {import('@playwright/test').Page } page - Playwright page
98+ * @returns {import('@playwright/test').FrameLocator }
99+ */
62100export const getLtiIFrame = ( page ) => {
63101 return page . frameLocator ( 'iframe[data-lti-launch="true"]' )
64102}
65103
66104let screenshotCount = 1
105+ /**
106+ * Take a screenshot of the provided locator and save it into the test
107+ * output directory. Files are numbered sequentially for the duration of the
108+ * process.
109+ *
110+ * @param {import('@playwright/test').Locator } locator - Locator to screenshot
111+ * @param {{ outputDir: string } } testInfo - Playwright `testInfo` object (only `outputDir` used)
112+ * @returns {Promise<void> }
113+ */
67114export const screenshot = async ( locator , testInfo ) => {
68- await locator . screenshot ( { path : `${ testInfo . outputDir } /${ screenshotCount } .png` , fullPage : true } )
115+ await locator . screenshot ( { path : `${ testInfo . outputDir } /${ screenshotCount } .png` , fullPage : true } )
69116 screenshotCount ++
70117}
71118
119+ /**
120+ * Dismiss the beta warning banner if present on the current page.
121+ * @param {import('@playwright/test').Page } page - Playwright page
122+ * @returns {Promise<void> }
123+ */
72124export const dismissBetaBanner = async ( page ) => {
73125 if ( page . url ( ) . includes ( 'beta' ) ) {
74126 const banner = page . getByRole ( 'button' , { name : 'Close warning' } )
75127 if ( await banner . isVisible ( ) ) {
76- await page . getByRole ( 'button' , { name : 'Close warning' } ) . click ( ) ;
128+ await page . getByRole ( 'button' , { name : 'Close warning' } ) . click ( )
77129 }
78130 }
79131}
80132
133+ /**
134+ * Wait for any `.view-spinner` elements inside the supplied frame locator to
135+ * disappear. Optionally provide an initial delay before checking.
136+ *
137+ * @param {import('@playwright/test').FrameLocator } frameLocator - Frame locator to query
138+ * @param {number } [initialDelay=1000] - milliseconds to wait before starting checks
139+ * @returns {Promise<void> }
140+ */
81141export const waitForNoSpinners = async ( frameLocator , initialDelay = 1000 ) => {
82- await new Promise ( r => setTimeout ( r , initialDelay ) ) ;
83- await expect ( frameLocator . locator ( '.view-spinner' ) ) . toHaveCount ( 0 , { timeout : 10000 } ) ;
142+ await new Promise ( r => setTimeout ( r , initialDelay ) )
143+ await expect ( frameLocator . locator ( '.view-spinner' ) ) . toHaveCount ( 0 , { timeout : 10000 } )
84144}
0 commit comments