diff --git a/package-lock.json b/package-lock.json index ffbee4a1e..bb2aee0d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22907,6 +22907,29 @@ "pend": "~1.2.0" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/fetch-cookie": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-0.11.0.tgz", @@ -23312,6 +23335,18 @@ "node": ">=0.4.x" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -32557,6 +32592,26 @@ "dev": true, "license": "MIT" }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -36506,6 +36561,12 @@ } } }, + "node_modules/puppeteer/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, "node_modules/puppeteer/node_modules/cosmiconfig": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", @@ -36532,6 +36593,33 @@ } } }, + "node_modules/puppeteer/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/puppeteer/node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/pvtsutils": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.5.tgz", @@ -38988,6 +39076,12 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/set-getter": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.1.tgz", @@ -40938,6 +41032,24 @@ "dev": true, "license": "MIT" }, + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "license": "MIT" + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -41040,23 +41152,24 @@ "license": "MIT" }, "node_modules/tough-cookie": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", - "integrity": "sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", "license": "BSD-3-Clause", "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", - "universalify": "^0.1.2" + "universalify": "^0.2.0", + "url-parse": "^1.5.3" }, "engines": { "node": ">=6" } }, "node_modules/tough-cookie/node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", "license": "MIT", "engines": { "node": ">= 4.0.0" @@ -41613,7 +41726,8 @@ "version": "4.3.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", - "devOptional": true, + "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -42781,7 +42895,6 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.2.tgz", "integrity": "sha512-3pRGuxRF5gpuZc0W+EpwQRmCD7gRqcDOMt688KmdlDAgAyaB1XlN0zq2njfDNm44XVdIouE7pZ6GzbdyH47uIQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -45441,11 +45554,14 @@ "@twilio/flex-dev-utils": "7.1.2", "@twilio/flex-plugins-api-client": "7.1.2", "axios": "^0.24.0", + "fetch-cookie": "^3.1.0", "lodash": "4.17.21", + "node-fetch": "^3.3.2", "package-json": "^7.0.0", "puppeteer": "^21.3.7", "replace-in-file": "^6.3.2", - "semver": "^7.3.5" + "semver": "^7.3.5", + "tough-cookie": "4" }, "devDependencies": { "@types/lodash": "4.14.177", @@ -45510,6 +45626,15 @@ "node": ">=8" } }, + "packages/flex-plugin-e2e-tests/node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "packages/flex-plugin-e2e-tests/node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -45532,6 +45657,16 @@ "node": ">=10" } }, + "packages/flex-plugin-e2e-tests/node_modules/fetch-cookie": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fetch-cookie/-/fetch-cookie-3.1.0.tgz", + "integrity": "sha512-s/XhhreJpqH0ftkGVcQt8JE9bqk+zRn4jF5mPJXWZeQMCI5odV9K+wEWYbnzFPHgQZlvPSMjS4n4yawWE8RINw==", + "license": "Unlicense", + "dependencies": { + "set-cookie-parser": "^2.4.8", + "tough-cookie": "^5.0.0" + } + }, "packages/flex-plugin-e2e-tests/node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", @@ -45602,6 +45737,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "packages/flex-plugin-e2e-tests/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, "packages/flex-plugin-e2e-tests/node_modules/normalize-url": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", @@ -45660,6 +45813,18 @@ "node": ">=10" } }, + "packages/flex-plugin-e2e-tests/node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, "packages/flex-plugin-scripts": { "name": "@twilio/flex-plugin-scripts", "version": "7.1.2", diff --git a/packages/flex-plugin-e2e-tests/package.json b/packages/flex-plugin-e2e-tests/package.json index 045c6a413..3c20da388 100644 --- a/packages/flex-plugin-e2e-tests/package.json +++ b/packages/flex-plugin-e2e-tests/package.json @@ -37,11 +37,14 @@ "@twilio/flex-dev-utils": "7.1.2", "@twilio/flex-plugins-api-client": "7.1.2", "axios": "^0.24.0", + "fetch-cookie": "^3.1.0", "lodash": "4.17.21", + "node-fetch": "^3.3.2", "package-json": "^7.0.0", "puppeteer": "^21.3.7", "replace-in-file": "^6.3.2", - "semver": "^7.3.5" + "semver": "^7.3.5", + "tough-cookie": "4.1.4" }, "devDependencies": { "@types/lodash": "4.14.177", diff --git a/packages/flex-plugin-e2e-tests/src/utils/browser.ts b/packages/flex-plugin-e2e-tests/src/utils/browser.ts index 1c23cecf3..acb8f2fe7 100644 --- a/packages/flex-plugin-e2e-tests/src/utils/browser.ts +++ b/packages/flex-plugin-e2e-tests/src/utils/browser.ts @@ -20,7 +20,13 @@ export class Browser { this._browser = await Puppeteer.launch({ headless: true, protocolTimeout: 300000, - args: ['--use-fake-ui-for-media-stream'], + args: [ + '--use-fake-ui-for-media-stream', + '--no-sandbox', + '--disable-setuid-sandbox', + '--disable-web-security', + '--disable-features=IsolateOrigins,site-per-process', + ], }); this._page = await this._browser.newPage(); await this._page.setRequestInterception(true); diff --git a/packages/flex-plugin-e2e-tests/src/utils/pages/view/twilio-console.ts b/packages/flex-plugin-e2e-tests/src/utils/pages/view/twilio-console.ts index a1b5a0899..4bdf79e67 100644 --- a/packages/flex-plugin-e2e-tests/src/utils/pages/view/twilio-console.ts +++ b/packages/flex-plugin-e2e-tests/src/utils/pages/view/twilio-console.ts @@ -4,6 +4,9 @@ import { logger } from '@twilio/flex-dev-utils'; import { testParams } from '../../../core'; import { Base } from './base'; import { sleep } from '../../timers'; +import fetch from 'node-fetch'; +import fetchCookie from 'fetch-cookie'; +import {CookieJar} from 'tough-cookie'; export class TwilioConsole extends Base { private static _loginForm = '#email'; @@ -34,6 +37,9 @@ export class TwilioConsole extends Base { * @param accountSid */ async login(flexPath: string, accountSid: string, localhostPort: number, firstLoad: boolean = true): Promise { + const cookieJar = new CookieJar(); + const fetchWithCookies = fetchCookie(fetch, cookieJar); + logger.info('firstload', firstLoad); const redirectUrl = this._flexBaseUrl.includes('localhost') ? TwilioConsole._createLocalhostUrl(localhostPort) @@ -41,47 +47,70 @@ export class TwilioConsole extends Base { const path = `console/flex/service-login/${accountSid}/?path=/${flexPath}&referer=${redirectUrl}`; await this.goto({ baseUrl: this._baseUrl, path }); + logger.info("Before firstload") if (firstLoad) { await this.elementVisible(TwilioConsole._loginForm, `Twilio Console's Login form`); - const csrfToken = await this.page.evaluate(async () => { - const response = await fetch('https://www.twilio.com/api/csrf', { - credentials: 'include', + logger.info("Login form is visible, proceeding with login"); + + let csrfToken = null; + try { + const csrfResponse = await fetchWithCookies('https://www.twilio.com/api/csrf', { + method: 'GET', + headers: { + 'Accept': 'application/json', + }, }); - const data = await response.json(); - return data.csrf; - }); - - if (csrfToken) { - const loginURL = `${this._baseUrl}/userauth/submitLoginPassword`; - await this.page.evaluate( - // eslint-disable-next-line @typescript-eslint/promise-function-async - (data: Record) => { - return fetch(data.url, { - headers: { - 'x-twilio-csrf': data.csrfToken, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - email: data.email, - password: data.password, - }), - method: 'POST', - credentials: 'include', - }); + logger.info('Fetched CSRF response:', csrfResponse.status); + const data = await csrfResponse.json(); + // @ts-ignore + csrfToken = data.csrf; + logger.info('CSRF response JSON:', data); + } catch (e) { + logger.error('CSRF fetch failed:', e); + throw new Error('CSRF token is null'); + } + + if (!csrfToken) { + logger.error("Unable to fetch CSRF token for Twilio Console login"); + throw new Error('CSRF token is null'); + } + + logger.info("CSRF token fetched, proceeding with login", csrfToken); + const loginURL = `${this._baseUrl}/userauth/submitLoginPassword`; + logger.info('loginURL', loginURL); + + try { + const response = await fetchWithCookies(loginURL, { + method: 'POST', + headers: { + 'x-twilio-csrf': csrfToken, + 'Content-Type': 'application/json', }, - { - url: loginURL, + body: JSON.stringify({ email: testParams.secrets.console.email, password: testParams.secrets.console.password, - csrfToken, - }, - ); + }), + }); + + const responseBody = await response.text(); + + logger.info('Login response status:', response.status); + const headersObj: Record = {}; + response.headers.forEach((value, key) => { + headersObj[key] = value; + }); + logger.info('Login response headers:', headersObj); + logger.info('Login response body:', responseBody); + + if (response.status !== 200) { + throw new Error(`Login failed with status ${response.status}`); + } - // Log in Flex via service login - logger.info('Logging in Flex via service login on first load'); + logger.info('Login successful, proceeding to Flex'); await this.goto({ baseUrl: this._baseUrl, path }); - } else { - throw new Error('Unable to fetch CSRF token to login to Twilio Console'); + } catch (e) { + logger.error('Login request failed:', e); + throw e; } }