diff --git a/packages/tests/src/ui-test/samples/sample-localdebug-contact-exporter.test.ts b/packages/tests/src/ui-test/samples/sample-localdebug-contact-exporter.test.ts index 999899235d..2114fa639e 100644 --- a/packages/tests/src/ui-test/samples/sample-localdebug-contact-exporter.test.ts +++ b/packages/tests/src/ui-test/samples/sample-localdebug-contact-exporter.test.ts @@ -23,15 +23,7 @@ class ContactExporterTestCase extends CaseFactory { sampledebugContext: SampledebugContext, teamsAppId: string ): Promise { - return await reopenPage( - sampledebugContext.context!, - teamsAppId, - Env.username, - Env.password, - undefined, - true, - true - ); + return await reopenPage(sampledebugContext.context!, teamsAppId); } } diff --git a/packages/tests/src/ui-test/samples/sample-localdebug-npm-search.test.ts b/packages/tests/src/ui-test/samples/sample-localdebug-npm-search.test.ts index dda62e73c9..453a928ab8 100644 --- a/packages/tests/src/ui-test/samples/sample-localdebug-npm-search.test.ts +++ b/packages/tests/src/ui-test/samples/sample-localdebug-npm-search.test.ts @@ -9,20 +9,27 @@ import { Page } from "playwright"; import { TemplateProject, LocalDebugTaskLabel } from "../../utils/constants"; import { validateNpm } from "../../utils/playwrightOperation"; import { CaseFactory } from "./sampleCaseFactory"; +import { SampledebugContext } from "./sampledebugContext"; class NpmSearchTestCase extends CaseFactory { override async onValidate( page: Page, - options?: { npmName: string } + options?: { npmName: string; context: SampledebugContext } ): Promise { - return await validateNpm(page, { npmName: options?.npmName }); + return await validateNpm(page, { + npmName: options?.npmName, + appName: options?.context.appName.substring(0, 10) || "", + }); } override async onCliValidate( page: Page, - options?: { npmName: string } + options?: { npmName: string; context: SampledebugContext } ): Promise { - return await validateNpm(page, { npmName: options?.npmName }); + return await validateNpm(page, { + npmName: options?.npmName, + appName: options?.context.appName.substring(0, 10) || "", + }); } } diff --git a/packages/tests/src/ui-test/samples/sample-remotedebug-npm-search.test.ts b/packages/tests/src/ui-test/samples/sample-remotedebug-npm-search.test.ts index 2399888b82..8c51c5db0d 100644 --- a/packages/tests/src/ui-test/samples/sample-remotedebug-npm-search.test.ts +++ b/packages/tests/src/ui-test/samples/sample-remotedebug-npm-search.test.ts @@ -9,20 +9,17 @@ import { Page } from "playwright"; import { TemplateProject } from "../../utils/constants"; import { validateNpm } from "../../utils/playwrightOperation"; import { CaseFactory } from "./sampleCaseFactory"; +import { SampledebugContext } from "./sampledebugContext"; class NpmSearchTestCase extends CaseFactory { override async onValidate( page: Page, - options?: { npmName: string } + options?: { npmName: string; context: SampledebugContext } ): Promise { - return await validateNpm(page, { npmName: options?.npmName }); - } - - override async onCliValidate( - page: Page, - options?: { npmName: string } - ): Promise { - return await validateNpm(page, { npmName: options?.npmName }); + return await validateNpm(page, { + npmName: options?.npmName, + appName: options?.context.appName.substring(0, 10) || "", + }); } } @@ -32,5 +29,5 @@ new NpmSearchTestCase( "v-ivanchen@microsoft.com", "dev", [], - { npmName: "axios", debug: "ttk" } + { npmName: "axios" } ).test(); diff --git a/packages/tests/src/utils/playwrightOperation.ts b/packages/tests/src/utils/playwrightOperation.ts index 2af1909584..2dba59aa7e 100644 --- a/packages/tests/src/utils/playwrightOperation.ts +++ b/packages/tests/src/utils/playwrightOperation.ts @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { BrowserContext, Page, Frame } from "playwright"; +import { BrowserContext, Page, Frame, ElementHandle } from "playwright"; import { assert, expect } from "chai"; import { Timeout, ValidationContent, TemplateProject } from "./constants"; import { RetryHandler } from "./retryHandler"; @@ -11,6 +11,7 @@ import path from "path"; import fs from "fs"; import { dotenvUtil } from "./envUtil"; import { startDebugging, startDebuggingAzure } from "./vscodeOperation"; +import { Env } from "./env"; export const debugInitMap: Record Promise> = { [TemplateProject.AdaptiveCard]: async () => { @@ -225,7 +226,7 @@ export async function initPage( state: "detached", }); console.log("[success] app loaded"); - await page.waitForTimeout(Timeout.shortTimeLoading); + await page.waitForTimeout(Timeout.longTimeWait); }); return page; @@ -234,8 +235,8 @@ export async function initPage( export async function reopenPage( context: BrowserContext, teamsAppId: string, - username: string, - password: string, + username?: string, + password?: string, options?: { teamsAppName?: string; dashboardFlag?: boolean; @@ -255,7 +256,7 @@ export async function reopenPage( page.waitForNavigation(), ]); - if (inputPassword) { + if (inputPassword && password) { // input password console.log(`fill in password`); await page.fill("input.input[type='password'][name='passwd']", password); @@ -301,7 +302,7 @@ export async function reopenPage( const addBtn = await page?.waitForSelector("button>span:has-text('Add')"); // dashboard template will have a popup - if (options?.dashboardFlag) { + if (options?.dashboardFlag && password) { console.log("Before popup"); const [popup] = await Promise.all([ page @@ -346,7 +347,7 @@ export async function reopenPage( state: "detached", }); } - await page.waitForTimeout(Timeout.shortTimeLoading); + await page.waitForTimeout(Timeout.longTimeWait); }); return page; @@ -1428,15 +1429,16 @@ export async function validateBot( } } -export async function validateNpm(page: Page, options?: { npmName?: string }) { +export async function validateNpm( + page: Page, + options: { npmName?: string; appName: string } +) { try { const searchPack = options?.npmName || "axios"; console.log("start to verify npm search"); await page.waitForTimeout(Timeout.shortTimeLoading); - const frameElementHandle = await page.waitForSelector( - "iframe.embedded-page-content" - ); - const frame = await frameElementHandle?.contentFrame(); + const frame = await page.waitForSelector("div#app"); + await messageExtensionActivate(page, options.appName); try { console.log("dismiss message"); await frame?.waitForSelector("div.ui-box"); @@ -1449,19 +1451,20 @@ export async function validateNpm(page: Page, options?: { npmName?: string }) { console.log("no message to dismiss"); } console.log("search npm ", searchPack); - const input = await frame?.waitForSelector("div.ui-box input.ui-box"); - await input?.type(searchPack); + const input = await page?.waitForSelector("div.ui-box input.ui-box"); + await input?.fill(searchPack); try { - const targetItem = await frame?.waitForSelector( + const targetItem = await page?.waitForSelector( `span:has-text("${searchPack}")` ); await targetItem?.click(); - await frame?.waitForSelector(`card span:has-text("${searchPack}")`); - await frame?.click('button[name="send"]'); + await page?.waitForSelector(`card span:has-text("${searchPack}")`); + const sendBtn = await frame?.waitForSelector('button[name="send"]'); + await sendBtn?.click(); console.log("verify npm search successfully!!!"); await page.waitForTimeout(Timeout.shortTimeLoading); } catch (error) { - await frame?.waitForSelector( + await page?.waitForSelector( 'div.ui-box span:has-text("Unable to reach app. Please try again.")' ); await page.screenshot({ @@ -1686,7 +1689,7 @@ export async function validateShareNow(page: Page) { console.log("start to verify share now"); await page.waitForTimeout(Timeout.shortTimeLoading); const frameElementHandle = await page.waitForSelector( - "iframe.embedded-page-content" + `iframe[name="embedded-page-container"]` ); const frame = await frameElementHandle?.contentFrame(); try { @@ -2066,11 +2069,12 @@ export async function validateContact( options?: { displayName?: string }, rerun = false ) { + let startBtn: ElementHandle | undefined; try { console.log("start to verify contact"); await page.waitForTimeout(Timeout.shortTimeLoading); const frameElementHandle = await page.waitForSelector( - "iframe.embedded-page-content" + "iframe[name='embedded-page-container']" ); const frame = await frameElementHandle?.contentFrame(); try { @@ -2083,58 +2087,74 @@ export async function validateContact( } catch (error) { console.log("no message to dismiss"); } - try { - if (!rerun) { - const startBtn = await frame?.waitForSelector( - 'button:has-text("Start")' - ); - await RetryHandler.retry(async () => { - console.log("Before popup"); - const [popup] = await Promise.all([ - page - .waitForEvent("popup") - .then((popup) => - popup - .waitForEvent("close", { - timeout: Timeout.playwrightConsentPopupPage, - }) - .catch(() => popup) - ) - .catch(() => {}), - startBtn?.click(), - ]); - console.log("after popup"); - - if (popup && !popup?.isClosed()) { - await popup - .click('button:has-text("Reload")', { - timeout: Timeout.playwrightConsentPageReload, - }) - .catch(() => {}); - await popup.click("input.button[type='submit'][value='Accept']"); - } - await frame?.waitForSelector( - `div:has-text("${options?.displayName}")` - ); - }); - } - await page.waitForTimeout(10000); + startBtn = await frame?.waitForSelector('button:has-text("Start")'); + if (startBtn) { + await RetryHandler.retry(async () => { + console.log("Before popup"); + const [popup] = await Promise.all([ + page + .waitForEvent("popup") + .then((popup) => + popup + .waitForEvent("close", { + timeout: Timeout.playwrightConsentPopupPage, + }) + .catch(() => popup) + ) + .catch(() => {}), + startBtn?.click(), + ]); + console.log("after popup"); - // verify add person - await addPerson(frame, options?.displayName || ""); - // verify delete person - await delPerson(frame, options?.displayName || ""); - } catch (e: any) { - console.log(`[Command not executed successfully] ${e.message}`); - await page.screenshot({ - path: getPlaywrightScreenshotPath("error"), - fullPage: true, + if (popup && !popup?.isClosed()) { + // if input password page is exist + if (rerun) { + try { + // input password + console.log(`fill in password`); + await popup.fill( + "input.input[type='password'][name='passwd']", + Env.password + ); + // sign in + await Promise.all([ + popup.click("input.button[type='submit'][value='Sign in']"), + popup.waitForNavigation(), + ]); + await popup.click("input.button[type='submit'][value='Accept']"); + try { + await popup?.close(); + } catch (error) { + console.log("popup is closed"); + } + } catch (error) { + await popup.screenshot({ + path: getPlaywrightScreenshotPath("login_error"), + fullPage: true, + }); + throw error; + } + } + await popup.screenshot({ + path: getPlaywrightScreenshotPath("login_after"), + fullPage: true, + }); + await popup + .click('button:has-text("Reload")', { + timeout: Timeout.playwrightConsentPageReload, + }) + .catch(() => {}); + await popup.click("input.button[type='submit'][value='Accept']"); + } + await frame?.waitForSelector(`div:has-text("${options?.displayName}")`); }); - throw e; } - - await RetryHandler.retry(async () => {}, 2); + await page.waitForTimeout(10000); + // verify add person + await addPerson(frame, options?.displayName || ""); + // verify delete person + await delPerson(frame, options?.displayName || ""); await page.waitForTimeout(Timeout.shortTimeLoading); } catch (error) { @@ -2154,9 +2174,10 @@ export async function validateGraphConnector( console.log("start to verify contact"); await page.waitForTimeout(Timeout.shortTimeLoading); const frameElementHandle = await page.waitForSelector( - "iframe.embedded-page-content" + "iframe[name='embedded-page-container']" ); const frame = await frameElementHandle?.contentFrame(); + const startBtn = await frame?.waitForSelector('button:has-text("Start")'); try { await RetryHandler.retry(async () => { console.log("Before popup"); @@ -2171,7 +2192,7 @@ export async function validateGraphConnector( .catch(() => popup) ) .catch(() => {}), - frame?.click('button:has-text("Start")', { + startBtn?.click({ timeout: Timeout.playwrightAddAppButton, force: true, noWaitAfter: true, @@ -2327,10 +2348,7 @@ export async function validateAdaptiveCard( options?: { context?: SampledebugContext; env?: "local" | "dev" } ) { try { - const frameElementHandle = await page.waitForSelector( - "iframe.embedded-page-content" - ); - const frame = await frameElementHandle?.contentFrame(); + const frame = await page.waitForSelector("div#app"); await frame?.waitForSelector("div.ui-box"); await page .click('button:has-text("Dismiss")', { @@ -2412,6 +2430,41 @@ export async function delPerson( ); } +export async function messageExtensionClean(page: Page, appName: string) { + let extBox: ElementHandle; + console.log("start to clean message extension"); + const appManagePage = await page.waitForSelector( + "button span:has-text('Apps')" + ); + console.log("click Apps"); + await appManagePage.click(); + await page.waitForTimeout(Timeout.shortTimeWait); + const appManageBtn = await page.waitForSelector( + "button:has-text('Manage your apps')" + ); + console.log("click Manage your apps"); + await appManageBtn.click(); + await page.waitForTimeout(Timeout.shortTimeWait); + const targetApp = await page.waitForSelector( + `div.treeitem span:has-text(${appName})` + ); + console.log("click target app"); + await targetApp.click(); + await page.waitForTimeout(Timeout.shortTimeWait); + const deleteBtn = await page.waitForSelector( + "button[data-tid=`uninstall-app`]" + ); + console.log("click delete button"); + await deleteBtn.click(); + await page.waitForTimeout(Timeout.shortTimeWait); + const dialog = await page.waitForSelector("div[role='dialog']"); + const confirmBtn = await dialog.waitForSelector("button:has-text('Remove')"); + console.log("click confirm button"); + await confirmBtn.click(); + await page.waitForTimeout(Timeout.shortTimeWait); + console.log("verify app removed successfully"); +} + export async function messageExtensionActivate(page: Page, appName: string) { console.log("start to activate message extension"); const extButton = await page.waitForSelector( @@ -2419,6 +2472,7 @@ export async function messageExtensionActivate(page: Page, appName: string) { ); console.log("click Actions and apps"); await extButton?.click(); + await page.waitForTimeout(Timeout.shortTimeLoading); const extBox = await page.waitForSelector("div.ui-popup__content__content"); // select secend second ul const extList = await extBox?.waitForSelector( @@ -2434,6 +2488,11 @@ export async function messageExtensionActivate(page: Page, appName: string) { if (text.includes(appName)) { console.log("click app:", appName); await item.click(); + await page.waitForTimeout(Timeout.shortTimeWait); + await page.screenshot({ + path: getPlaywrightScreenshotPath("ext_actived"), + fullPage: true, + }); break; } } @@ -2586,10 +2645,7 @@ export async function validateLargeNotificationBot( notificationEndpoint = "http://127.0.0.1:3978/api/notification" ) { try { - const frameElementHandle = await page.waitForSelector( - "iframe.embedded-page-content" - ); - const frame = await frameElementHandle?.contentFrame(); + const frame = await page.waitForSelector("div#app"); await frame?.waitForSelector("div.ui-box"); await page .click('button:has-text("Dismiss")', {