diff --git a/.gitignore b/.gitignore index dad4de13..81d910bd 100644 --- a/.gitignore +++ b/.gitignore @@ -138,3 +138,5 @@ configs/test-html-results/ browserstackSetupConfig.json configs/playwright-browserstack-sdk.config.js playwright-browserstack-sdk.config.temp.json + +.auth/ diff --git a/selectors/bacom-blog/sharepoint.page.js b/selectors/bacom-blog/sharepoint.page.js index 4cfac237..2d7053a0 100644 --- a/selectors/bacom-blog/sharepoint.page.js +++ b/selectors/bacom-blog/sharepoint.page.js @@ -11,39 +11,88 @@ export default class Sharepoint { this.insertPageBreakButton = this.iframe.locator('#InsertPageBreak'); this.homeButton = this.iframe.locator('#Home'); this.undoButton = this.iframe.locator('#UndoRedo > button:first-of-type'); + this.editingElement = this.iframe.locator('#WACViewPanel_EditingElement'); + this.documentTitle = this.iframe.locator('button#documentTitle'); + this.dialogPanel = this.iframe.locator('#WACDialogPanel'); + this.dialogText = this.iframe.locator('#WACDialogTextPanel'); + this.navButton = this.iframe.locator('#FishBowlNavButton'); + this.pageBreaks = this.iframe.locator('span.PageBreakTextSpan'); } /** - * @description Adds a page break by selecting the insert page break button under - * the inserts tab and then saves the file. + * @description Gets the text of the dialog box. + * @returns {Promise} The text of the dialog box. + */ + async getDialogText() { + if (await this.dialogText.isVisible()) { + const dialogText = await this.dialogText.textContent(); + return dialogText; + } + return ''; + } + + /** + * @description Waits for the page to load by checking for key elements. + * @returns {Promise} True if the page is loaded, false otherwise. + */ + async waitForLoad() { + await this.page.waitForLoadState('domcontentloaded'); + await expect(this.homeButton).toBeVisible({ timeout: 1000 * 30 }); + await expect(this.navButton).toBeVisible({ timeout: 1000 * 30 }); + + // check that there is no dialog box blocking the page + return !await this.dialogPanel.isVisible(); + } + + /** + * @description Saves the file by simulating the keyboard shortcut Control+S. + */ + async saveFile() { + await this.page.keyboard.press('Control+S'); + + const saveStatus = await this.documentTitle.locator('div[aria-label]').first(); + await expect.soft(saveStatus).toHaveAttribute('aria-label', /.*Last saved: Just now/, { timeout: 1000 * 30 }); + } + + /** + * @description Adds a page break by selecting the insert page break button under the inserts tab. */ async addPageBreak() { + const pageBreakCount = await this.pageBreaks.count(); + // Make sure the button is visible before clicking await expect(async () => { await this.insertButton.click(); - await expect(this.insertPageBreakButton).toBeVisible(); - await this.insertPageBreakButton.click(); - await this.homeButton.click(); - await expect(this.undoButton).toBeVisible(); - await expect(this.undoButton).toBeEnabled(); + await expect(this.insertPageBreakButton).toBeVisible({ timeout: 1000 }); }).toPass(); - await this.page.keyboard.press('Control+S'); - await this.page.waitForTimeout(2000); + // We can not trust the button to work immediately + await expect(async () => { + if (await this.insertPageBreakButton.isDisabled()) { + await this.editingElement.focus(); + await this.page.keyboard.press('ArrowDown'); + } + if (await this.insertPageBreakButton.isEnabled()) { + await this.insertPageBreakButton.click(); + } + + await expect(this.pageBreaks).not.toHaveCount(pageBreakCount, { timeout: 1000 }); + }).toPass(); } /** - * @description Undos the previous change in session by selecting the undo button - * under the home tab and then saves the file. + * @description Undos all previous changes in session by selecting the undo button under the home tab. */ async undoChanges() { - if (!this.undoButton.isVisible()) this.homeButton.click(); + await expect(async () => { + await this.homeButton.click(); + await expect(this.undoButton).toBeVisible({ timeout: 1000 }); + }).toPass(); + + await expect(this.undoButton).toBeEnabled(); await expect(async () => { await this.undoButton.click(); - await expect(this.undoButton).toBeDisabled(); + await expect(this.undoButton).toBeDisabled({ timeout: 1000 }); }).toPass(); - - await this.page.keyboard.press('Control+S'); - await this.page.waitForTimeout(2000); } } diff --git a/tests/bacom-blog/edit-sharepoint-doc.test.js b/tests/bacom-blog/edit-sharepoint-doc.test.js index 7b16ef7f..8090008e 100644 --- a/tests/bacom-blog/edit-sharepoint-doc.test.js +++ b/tests/bacom-blog/edit-sharepoint-doc.test.js @@ -3,36 +3,45 @@ import fs from 'fs'; import Sharepoint from '../../selectors/bacom-blog/sharepoint.page.js'; const sharepointBacomBlogDrafts = 'https://adobe.sharepoint.com/sites/BizWeb/Shared%20Documents/Forms/AllItems.aspx'; -const bacomBlogAdminUrl = 'https://admin.hlx.page/status/adobecom/bacom-blog/main/'; -const bacomBlogHlx = 'https://main--bacom-blog--adobecom.hlx.live/'; +const bacomBlogAdminUrl = 'https://admin.hlx.page/status/adobecom/bacom-blog/main'; const data = JSON.parse(fs.readFileSync('./data/bacom-blog/stagedBlogUrls.json', 'utf8')); const testPages = Object.keys(data).map((key) => data[key][1]); let page; +let context; let sharepoint; +const authFile = 'tests/bacom-blog/.auth/user.json'; + test.describe('Sharepoint editing', { tag: '@sp' }, async () => { test.beforeAll(async ({ browser }) => { - page = await browser.newPage(); + const options = fs.existsSync(authFile) ? { storageState: authFile } : {}; + context = await browser.newContext(options); + page = await context.newPage(); sharepoint = new Sharepoint(page); // TODO: Automated okta login // For now, we need to sign into okta manually await page.goto(sharepointBacomBlogDrafts); - await page.waitForTimeout(160000); + await page.waitForURL(sharepointBacomBlogDrafts, { timeout: 1000 * 60 * 2 }); + + await context.storageState({ path: authFile }); + }); + + test.afterAll(async () => { + await page.close(); }); testPages.forEach(async (url) => { await test(`Editing a docx in sharepoint - ${url} `, async () => { - await test.setTimeout(10000 * 1000); + await test.setTimeout(1000 * 60 * 2); // Set each test timeout to 2 minutes await test.step('1. Go to the docx.', async () => { - const relative = url.replace(bacomBlogHlx, ''); - const adminPage = `${bacomBlogAdminUrl}${relative}?editUrl=auto`; - await page.goto(adminPage); - await page.waitForLoadState('domcontentloaded'); - const adminPageContent = await page.locator('pre').textContent(); - const docxUrl = JSON.parse(adminPageContent).edit.url; + const { pathname } = new URL(url); + const adminPage = `${bacomBlogAdminUrl}${pathname}?editUrl=auto`; + const response = await page.evaluate(async (api) => fetch(api) + .then((req) => (req.ok ? req.json() : Promise.reject(req))), adminPage); + const docxUrl = response?.edit.url; console.log(`[Test page]: ${url}`); console.log(`[Admin page]: ${adminPage}`); @@ -40,20 +49,35 @@ test.describe('Sharepoint editing', { tag: '@sp' }, async () => { if (docxUrl === undefined) { console.log(`Unable to find edit url for ${url}\n`); - test.skip(); + test.skip(true, 'Unable to find edit url'); } - await page.goto(docxUrl); - await page.waitForLoadState('domcontentloaded'); }); - await test.step('2. Make an edit and save.', async () => { + await test.step('2. Load the docx.', async () => { + const loaded = await sharepoint.waitForLoad(); + if (!loaded) { + const status = await sharepoint.getDialogText(); + console.log(status); + test.skip(true, status); + } + }); + + await test.step('3. Make an edit.', async () => { await sharepoint.addPageBreak(); }); - await test.step('3. Undo the change and save.', async () => { + await test.step('4. save document.', async () => { + await sharepoint.saveFile(); + }); + + await test.step('5. Undo the change.', async () => { await sharepoint.undoChanges(); }); + + await test.step('6. save document.', async () => { + await sharepoint.saveFile(); + }); }); }); });