diff --git a/src/extension/background.js b/src/extension/background.js index aa8ba713d..ac5c19d86 100644 --- a/src/extension/background.js +++ b/src/extension/background.js @@ -29,7 +29,6 @@ import { getGitHubSettings, setConfig, getConfig, - storeAuthToken, updateProjectConfigs, } from './utils.js'; @@ -117,8 +116,10 @@ async function guessIfFranklinSite({ id }) { }); // listen for response message from tab const listener = ({ isFranklinSite }) => { - chrome.runtime.onMessage.removeListener(listener); - resolve(isFranklinSite); + if (typeof isFranklinSite === 'boolean') { + chrome.runtime.onMessage.removeListener(listener); + resolve(isFranklinSite); + } }; chrome.runtime.onMessage.addListener(listener); }); @@ -330,6 +331,75 @@ function checkViewDocSource(id) { }); } +/** + * Sets the x-auth-token header for all requests to admin.hlx.page if project config + * has an auth token. + */ +async function updateAdminAuthHeaderRules() { + try { + // remove all rules first + await chrome.declarativeNetRequest.updateSessionRules({ + removeRuleIds: (await chrome.declarativeNetRequest.getSessionRules()) + .map((rule) => rule.id), + }); + // find projects with auth tokens and add rules for each + let id = 1; + const projects = await getConfig('sync', 'hlxSidekickProjects') || []; + const addRules = []; + const projectConfigs = await Promise.all(projects.map((handle) => getConfig('sync', handle))); + projectConfigs.forEach(({ owner, repo, authToken }) => { + if (authToken) { + addRules.push({ + id, + priority: 1, + action: { + type: 'modifyHeaders', + requestHeaders: [{ + operation: 'set', + header: 'x-auth-token', + value: authToken, + }], + }, + condition: { + regexFilter: `^https://admin.hlx.page/[a-z]+/${owner}/${repo}/.*`, + requestDomains: ['admin.hlx.page'], + requestMethods: ['get', 'post', 'delete'], + resourceTypes: ['xmlhttprequest'], + }, + }); + id += 1; + log.debug('added admin auth header rule for ', owner, repo); + } + }); + if (addRules.length > 0) { + await chrome.declarativeNetRequest.updateSessionRules({ + addRules, + }); + log.debug(`setAdminAuthHeaderRule: ${addRules.length} rule(s) set`); + } + } catch (e) { + log.error(`updateAdminAuthHeaderRules: ${e.message}`); + } +} + +async function storeAuthToken(owner, repo, token) { + // find config tab with owner/repo + const project = await getProject({ owner, repo }); + if (project) { + if (token) { + project.authToken = token; + } else { + delete project.authToken; + } + await setProject(project); + log.debug(`updated auth token for ${owner}--${repo}`); + } else { + log.debug(`unable to update auth token for ${owner}--${repo}: no such config`); + } + // auth token changed, set/update admin auth header + updateAdminAuthHeaderRules(); +} + /** * Adds the listeners for the extension. */ @@ -338,6 +408,7 @@ function checkViewDocSource(id) { log.info(`sidekick extension installed (${reason})`); await updateHelpContent(); await updateProjectConfigs(); + await updateAdminAuthHeaderRules(); }); // register message listener @@ -431,6 +502,22 @@ function checkViewDocSource(id) { actions[actionFromTab](tab); } }); + + // listen for delete auth token calls from the content window + chrome.runtime.onMessage.addListener(async ({ deleteAuthToken }, { tab }) => { + // check if message contains project config and is sent from tab + if (tab && tab.id && typeof deleteAuthToken === 'object') { + const { owner, repo } = deleteAuthToken; + await storeAuthToken(owner, repo, ''); + } + }); + + // for local debugging of header modification rules: + // 1. add "declarativeNetRequestFeedback" to permissions in manifest.json + // 2. uncomment the following 3 lines: + // chrome.declarativeNetRequest.onRuleMatchedDebug.addListener(({ request, rule }) => { + // console.log('rule matched', request.method, request.url, rule.ruleId); + // }); })(); // announce sidekick display state diff --git a/src/extension/manifest.json b/src/extension/manifest.json index 60311a8fc..55ea955b7 100644 --- a/src/extension/manifest.json +++ b/src/extension/manifest.json @@ -8,12 +8,14 @@ "options_page": "options.html", "description": "__MSG_description__", "permissions": [ + "activeTab", "contextMenus", + "declarativeNetRequest", "scripting", - "storage", - "activeTab" + "storage" ], "host_permissions": [ + "https://www.hlx.live/tools/sidekick/*", "http://localhost:3000/*", "https://*/*" ], diff --git a/src/extension/module.js b/src/extension/module.js index 13cd67cde..9b044b436 100644 --- a/src/extension/module.js +++ b/src/extension/module.js @@ -888,15 +888,12 @@ * @param {object} config * @returns {object} */ - function getAdminFetchOptions({ authToken }) { + function getAdminFetchOptions() { const opts = { cache: 'no-store', credentials: 'include', headers: {}, }; - if (authToken) { - opts.headers['x-auth-token'] = authToken; - } return opts; } @@ -1791,6 +1788,18 @@ } } + async function checkProfileStatus(sk, status) { + const url = getAdminUrl(sk.config, 'profile'); + const opts = getAdminFetchOptions(sk.config); + return fetch(url, opts) + .then((res) => res.json()) + .then((json) => { + console.log(json); + return json.status === status; + }) + .catch(() => false); + } + /** * Logs the user in. * @private @@ -1799,70 +1808,50 @@ */ function login(sk, selectAccount) { sk.showWait(); - const loginUrl = getAdminUrl( - sk.config, - 'login', - sk.isProject() ? sk.location.pathname : '', - ); - loginUrl.searchParams.set('loginRedirect', 'https://www.hlx.live/tools/sidekick/login-success'); + const loginUrl = getAdminUrl(sk.config, 'login'); const extensionId = window.chrome?.runtime?.id; - if (extensionId) { + const authHeaderEnabled = extensionId && !window.navigator.vendor.includes('Apple'); + if (authHeaderEnabled) { loginUrl.searchParams.set('extensionId', extensionId); + } else { + loginUrl.searchParams.set( + 'loginRedirect', + 'https://www.hlx.live/tools/sidekick/login-success', + ); } if (selectAccount) { loginUrl.searchParams.set('selectAccount', true); } - const profileUrl = new URL('https://admin.hlx.page/profile'); - if (sk.config.adminVersion) { - profileUrl.searchParams.append('hlx-admin-version', sk.config.adminVersion); - } const loginWindow = window.open(loginUrl.toString()); - async function checkLoggedIn() { - if ((await fetch(profileUrl.href, getAdminFetchOptions(sk.config))).ok) { - window.setTimeout(() => { - if (!loginWindow.closed) { - loginWindow.close(); - } - }, 500); - delete sk.status.status; - sk.addEventListener('statusfetched', () => sk.hideModal(), { once: true }); - sk.fetchStatus(); - fireEvent(sk, 'loggedin'); - return true; - } - return false; - } + let attempts = 0; - let seconds = 0; - const loginCheck = window.setInterval(async () => { - // give up after 2 minutes or window closed - if (seconds >= 120 || loginWindow.closed) { - window.clearInterval(loginCheck); - loginWindow.close(); - // last check - if (await checkLoggedIn()) { + async function checkLoggedIn() { + if (loginWindow.closed) { + attempts += 1; + // try 5 times after login window has been closed + if (await checkProfileStatus(sk, 200)) { + // logged in, stop checking + delete sk.status.status; + sk.addEventListener('statusfetched', () => sk.hideModal(), { once: true }); + sk.fetchStatus(); + fireEvent(sk, 'loggedin'); return; } - - if (seconds >= 120) { + if (attempts >= 5) { + // give up after 5 attempts sk.showModal({ message: i18n(sk, 'error_login_timeout'), sticky: true, level: 1, }); - } else { - sk.showModal({ - messsage: i18n(sk, 'error_login_aborted'), - }); + return; } } - - seconds += 1; - if (await checkLoggedIn()) { - window.clearInterval(loginCheck); - } - }, 1000); + // try again after 1s + window.setTimeout(checkLoggedIn, 1000); + } + window.setTimeout(checkLoggedIn, 1000); } /** @@ -1871,29 +1860,47 @@ * @param {Sidekick} sk The sidekick */ function logout(sk) { - const logoutUrl = new URL('https://admin.hlx.page/logout'); - if (sk.config.adminVersion) { - logoutUrl.searchParams.append('hlx-admin-version', sk.config.adminVersion); - } - - fetch(logoutUrl.href, { - ...getAdminFetchOptions(sk.config), - }) - .then(() => { - delete sk.config.authToken; - sk.status = { - loggedOut: true, - }; - }) - .then(() => fireEvent(sk, 'loggedout')) - .then(() => sk.fetchStatus()) - .catch((e) => { - console.error('logout failed', e); - sk.showModal({ - message: i18n(sk, 'error_logout_error'), - level: 0, - }); - }); + sk.showWait(); + const logoutUrl = getAdminUrl(sk.config, 'logout'); + const extensionId = window.chrome?.runtime?.id; + if (extensionId && !window.navigator.vendor.includes('Apple')) { // exclude safari + logoutUrl.searchParams.set('extensionId', extensionId); + } else { + logoutUrl.searchParams.set( + 'logoutRedirect', + 'https://www.hlx.live/tools/sidekick/logout-success', + ); + } + const logoutWindow = window.open(logoutUrl.toString()); + + let attempts = 0; + + async function checkLoggedOut() { + if (logoutWindow.closed) { + attempts += 1; + // try 5 times after login window has been closed + if (await checkProfileStatus(sk, 401)) { + delete sk.status.profile; + delete sk.config.authToken; + sk.addEventListener('statusfetched', () => sk.hideModal(), { once: true }); + sk.fetchStatus(); + fireEvent(sk, 'loggedout'); + return; + } + if (attempts >= 5) { + // give up after 5 attempts + sk.showModal({ + message: i18n(sk, 'error_logout_error'), + sticky: true, + level: 1, + }); + return; + } + } + // try again after 1s + window.setTimeout(checkLoggedOut, 1000); + } + window.setTimeout(checkLoggedOut, 1000); } /** @@ -2643,7 +2650,10 @@ throw new Error('error_status_invalid'); } }) - .then((json) => Object.assign(this.status, json)) + .then((json) => { + this.status = json; + return json; + }) .then((json) => fireEvent(this, 'statusfetched', json)) .catch(({ message }) => { this.status.error = message; diff --git a/src/extension/sidekick.js b/src/extension/sidekick.js index 28c7d92c5..b2110b2a1 100644 --- a/src/extension/sidekick.js +++ b/src/extension/sidekick.js @@ -21,7 +21,6 @@ import { setConfig, setDisplay, i18n, - storeAuthToken, } from './utils.js'; export default async function injectSidekick(config, display) { @@ -105,11 +104,6 @@ export default async function injectSidekick(config, display) { sk.addEventListener('hidden', () => { setDisplay(false); }); - sk.addEventListener('loggedout', async () => { - // user clicked logout, delete the authToken from the config - log.debug(`removing authToken from config ${owner}/${repo}`); - await storeAuthToken(owner, repo, ''); - }); const helpOptOut = await getConfig('sync', 'hlxSidekickHelpOptOut'); if (!helpOptOut) { // find next unacknowledged help topic with matching condition diff --git a/src/extension/utils.js b/src/extension/utils.js index 99f78f1e6..da36d7058 100644 --- a/src/extension/utils.js +++ b/src/extension/utils.js @@ -310,6 +310,7 @@ export async function getProject(project) { export async function setProject(project, cb) { const { owner, repo } = project; const handle = `${owner}/${repo}`; + // update project config await setConfig('sync', { [handle]: project, }); @@ -320,6 +321,7 @@ export async function setProject(project, cb) { await setConfig('sync', { hlxSidekickProjects: projects }); } log.info('updated project', project); + if (typeof cb === 'function') { cb(project); } @@ -346,6 +348,9 @@ export async function deleteProject(handle, cb) { const i = projects.indexOf(handle); if (i >= 0) { if (confirm(i18n('config_delete_confirm'))) { + // delete admin auth header rule + const [owner, repo] = handle.split('/'); + chrome.runtime.sendMessage({ deleteAuthToken: { owner, repo } }); // delete the project entry await removeConfig('sync', handle); // remove project entry from index @@ -380,22 +385,6 @@ export function toggleDisplay(cb) { }); } -export async function storeAuthToken(owner, repo, token) { - // find config tab with owner/repo - const project = await getProject({ owner, repo }); - if (project) { - if (token) { - project.authToken = token; - } else { - delete project.authToken; - } - await setProject(project); - log.debug(`updated auth token for ${owner}--${repo}`); - } else { - log.warn(`unable to update auth token for ${owner}--${repo}: no such config`); - } -} - export async function updateProjectConfigs() { const configs = await getConfig('sync', 'hlxSidekickConfigs'); const projects = await getConfig('sync', 'hlxSidekickProjects'); diff --git a/src/safari/helix-sidekick-extension.xcodeproj/project.pbxproj b/src/safari/helix-sidekick-extension.xcodeproj/project.pbxproj index 3ab6143e2..e2bbdbf8a 100644 --- a/src/safari/helix-sidekick-extension.xcodeproj/project.pbxproj +++ b/src/safari/helix-sidekick-extension.xcodeproj/project.pbxproj @@ -719,7 +719,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = "6.12.5"; + CURRENT_PROJECT_VERSION = "6.13.1"; DEVELOPMENT_TEAM = JQ525L2MZD; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "iOS (Extension)/Info.plist"; @@ -731,7 +731,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = "6.12.5"; + MARKETING_VERSION = "6.13.1"; OTHER_LDFLAGS = ( "-framework", SafariServices, @@ -750,7 +750,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = "6.12.5"; + CURRENT_PROJECT_VERSION = "6.13.1"; DEVELOPMENT_TEAM = JQ525L2MZD; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "iOS (Extension)/Info.plist"; @@ -762,7 +762,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = "6.12.5"; + MARKETING_VERSION = "6.13.1"; OTHER_LDFLAGS = ( "-framework", SafariServices, @@ -785,7 +785,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = "6.12.5"; + CURRENT_PROJECT_VERSION = "6.13.1"; DEVELOPMENT_TEAM = JQ525L2MZD; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "iOS (App)/Info.plist"; @@ -801,7 +801,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = "6.12.5"; + MARKETING_VERSION = "6.13.1"; OTHER_LDFLAGS = ( "-framework", SafariServices, @@ -824,7 +824,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = "6.12.5"; + CURRENT_PROJECT_VERSION = "6.13.1"; DEVELOPMENT_TEAM = JQ525L2MZD; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = "iOS (App)/Info.plist"; @@ -840,7 +840,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = "6.12.5"; + MARKETING_VERSION = "6.13.1"; OTHER_LDFLAGS = ( "-framework", SafariServices, @@ -862,7 +862,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = "macOS (Extension)/AEM Sidekick.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = "6.12.5"; + CURRENT_PROJECT_VERSION = "6.13.1"; DEVELOPMENT_TEAM = JQ525L2MZD; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; @@ -875,7 +875,7 @@ "@executable_path/../../../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = "6.12.5"; + MARKETING_VERSION = "6.13.1"; OTHER_LDFLAGS = ( "-framework", SafariServices, @@ -894,7 +894,7 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = "macOS (Extension)/AEM Sidekick.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = "6.12.5"; + CURRENT_PROJECT_VERSION = "6.13.1"; DEVELOPMENT_TEAM = JQ525L2MZD; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; @@ -907,7 +907,7 @@ "@executable_path/../../../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = "6.12.5"; + MARKETING_VERSION = "6.13.1"; OTHER_LDFLAGS = ( "-framework", SafariServices, @@ -929,7 +929,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = "macOS (App)/AEM Sidekick.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = "6.12.5"; + CURRENT_PROJECT_VERSION = "6.13.1"; DEVELOPMENT_TEAM = JQ525L2MZD; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; @@ -943,7 +943,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = "6.12.5"; + MARKETING_VERSION = "6.13.1"; OTHER_LDFLAGS = ( "-framework", SafariServices, @@ -966,7 +966,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = "macOS (App)/AEM Sidekick.entitlements"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = "6.12.5"; + CURRENT_PROJECT_VERSION = "6.13.1"; DEVELOPMENT_TEAM = JQ525L2MZD; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; @@ -980,7 +980,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = "6.12.5"; + MARKETING_VERSION = "6.13.1"; OTHER_LDFLAGS = ( "-framework", SafariServices, diff --git a/test/SidekickTest.js b/test/SidekickTest.js index f0f67e77e..9918a39bf 100644 --- a/test/SidekickTest.js +++ b/test/SidekickTest.js @@ -85,7 +85,7 @@ import { * @param {string} o.env=preview The environment (preview or live) * @param {string} o.type=html The content type of the requested resource (html, xml or json) * @param {string} o.fixture=generic.html The fixture file to use as test bed - * @param {number} o.sleep=0 The number of milliseconds to wait after loading the sidekick + * @param {number} o.sleep=500 The number of milliseconds to wait after loading the sidekick * @param {string} o.plugin A plugin to execute after loading the sidekick * @param {number} o.pluginSleep=0 The number of milliseconds to wait after executing a plugin * @param {boolean} acceptDialogs=false Defines whether dialogs will be accepted or dismissed @@ -140,7 +140,7 @@ export class SidekickTest extends EventEmitter { this.env = o.env || 'preview'; this.type = o.type || 'html'; this.fixture = o.fixture || 'generic.html'; - this.sleep = o.sleep ?? 0; + this.sleep = o.sleep ?? 500; this.plugin = o.plugin; this.pluginSleep = o.pluginSleep ?? 0; this.acceptDialogs = o.acceptDialogs || false; diff --git a/test/bulk-preview.test.js b/test/bulk-preview.test.js index f4708ce50..298066756 100644 --- a/test/bulk-preview.test.js +++ b/test/bulk-preview.test.js @@ -63,6 +63,7 @@ describe('Test bulk preview plugin', () => { const { plugins } = await new SidekickTest({ browser, page, + sleep: 1000, fixture: TESTS[0].fixture, url: setup.getUrl('edit', 'admin'), pre: (p) => p.evaluate(() => { diff --git a/test/extension/chromeMock.js b/test/extension/chromeMock.js index 3ae2ad2ac..5d899fff2 100644 --- a/test/extension/chromeMock.js +++ b/test/extension/chromeMock.js @@ -51,6 +51,7 @@ export default { getManifest: async () => readFile({ path: '../../src/extension/manifest.json' }).then((mf) => JSON.parse(mf)), getURL: (path) => `chrome-extension://${ID}${path}`, lastError: null, + sendMessage: () => {}, }, storage: { sync: new StorageMock({ @@ -73,4 +74,8 @@ export default { test: 'test', }), }, + declarativeNetRequest: { + getSessionRules: async () => ([]), + updateSessionRules: async () => undefined, + }, }; diff --git a/test/extension/utils.test.js b/test/extension/utils.test.js index f6d07b003..92b22dd08 100644 --- a/test/extension/utils.test.js +++ b/test/extension/utils.test.js @@ -227,11 +227,11 @@ describe('Test extension utils', () => { it('deleteProject', async () => { const spy = sandbox.spy(window.chrome.storage.sync, 'set'); const deleted = await new Promise((resolve) => { - utils.deleteProject('test/add-project', resolve); + utils.deleteProject('adobe/blog', resolve); }); expect(deleted).to.be.true; expect(spy.calledWith({ - hlxSidekickProjects: ['adobe/blog'], + hlxSidekickProjects: ['test/add-project'], })).to.be.true; }); diff --git a/test/login.test.js b/test/login.test.js index 5adb3aa07..0d5a4bce4 100644 --- a/test/login.test.js +++ b/test/login.test.js @@ -13,7 +13,7 @@ import assert from 'assert'; import { - DEBUG, IT_DEFAULT_TIMEOUT, Nock, sleep, TestBrowser, + IT_DEFAULT_TIMEOUT, Nock, TestBrowser, } from './utils.js'; import { SidekickTest } from './SidekickTest.js'; @@ -47,55 +47,40 @@ describe('Test sidekick login', () => { const test = new SidekickTest({ browser, page, - waitPopup: 2000, + pluginSleep: 2000, plugin: 'user-login', loadModule: true, }); nock('https://admin.hlx.page') .get('/status/adobe/blog/main/en/topics/bla?editUrl=auto') - .times(2) + .twice() .reply(function req() { if (this.req.headers.cookie === 'auth_token=foobar') { loggedIn = true; - return [200, '{}', { 'content-type': 'application/json' }]; + return [200, '{ "status": 200}', { 'content-type': 'application/json' }]; } - return [401]; + return [401, '{ "status": 401 }', { 'content-type': 'application/json' }]; }) - .get('/login/adobe/blog/main/en/topics/bla?loginRedirect=https%3A%2F%2Fwww.hlx.live%2Ftools%2Fsidekick%2Flogin-success') - .times(DEBUG ? 2 : 1) // when dev-tools are enabled, browser makes 2 requests. - .delay(1500) // delay so that 2 requests are made - .reply(200, 'logged in!', { + .get('/login/adobe/blog/main?loginRedirect=https%3A%2F%2Fwww.hlx.live%2Ftools%2Fsidekick%2Flogin-success') + .reply(200, 'logged in', { 'set-cookie': 'auth_token=foobar; Path=/; HttpOnly; Secure; SameSite=None', }) - .get('/profile') - .times(2) + .get('/profile/adobe/blog/main') .reply(function req() { if (this.req.headers.cookie === 'auth_token=foobar') { - return [200, '{}', { 'content-type': 'application/json' }]; + return [200, '{ "status": 200 }', { 'content-type': 'application/json' }]; } - return [401]; - }); + return [401, '{ "status": 401 }', { 'content-type': 'application/json' }]; + }) + // in debug mode, the browser requests /favicon.ico + .get('/favicon.ico') + .optionally() + .reply(404); await test.run(); - // wait until login window closes - let loginClosed = false; - await Promise.race([ - new Promise((resolve) => { - page.browser().on('targetdestroyed', async (target) => { - const targetUrl = target.url(); - if (targetUrl.startsWith('https://admin.hlx.page/login/adobe/blog/main/en/topics/bla')) { - loginClosed = true; - resolve(); - } - }); - }), - sleep(2000), - ]); - assert.ok(loggedIn, 'Sidekick did not send auth cookie.'); - assert.ok(loginClosed, 'Sidekick did not close login window.'); }).timeout(IT_DEFAULT_TIMEOUT); it('Opens login window and shows aborted modal', async () => { @@ -103,26 +88,20 @@ describe('Test sidekick login', () => { browser, page, plugin: 'user-login', - pluginSleep: 2000, + pluginSleep: 7000, // sidekick tries 5 times before showing the login aborted modal loadModule: true, }); nock('https://admin.hlx.page') .get('/status/adobe/blog/main/en/topics/bla?editUrl=auto') - .reply(401) - .get('/profile') - .times(2) - .reply(401) - .get('/login/adobe/blog/main/en/topics/bla') - .query({ - loginRedirect: 'https://www.hlx.live/tools/sidekick/login-success', - }) - .reply(200, 'not logged in!'); + .reply(401, '{ "status": 401 }', { 'content-type': 'application/json' }) + .get('/login/adobe/blog/main?loginRedirect=https%3A%2F%2Fwww.hlx.live%2Ftools%2Fsidekick%2Flogin-success') + .reply(200, 'not logged in!') + .get('/profile/adobe/blog/main') + .times(5) + .reply(401, '{ "status": 401 }', { 'content-type': 'application/json' }); - const { popupTarget } = await test.run(); - - // close login window - await (await popupTarget.page()).close(); + await test.run(); // wait for 'aborted' modal try { diff --git a/test/logout.test.js b/test/logout.test.js index 7cc1e05c3..7fc1b02b3 100644 --- a/test/logout.test.js +++ b/test/logout.test.js @@ -45,14 +45,18 @@ describe('Test sidekick logout', () => { it('Logout removes auth token from config', async () => { nock.admin(new Setup('blog')); nock('https://admin.hlx.page') - .get('/logout') - .reply(200, {}); - nock.admin(new Setup('blog')); + .get('/status/adobe/blog/main/en/topics/bla?editUrl=auto') + .reply(200, { status: 200 }) + .get('/logout/adobe/blog/main?logoutRedirect=https%3A%2F%2Fwww.hlx.live%2Ftools%2Fsidekick%2Flogout-success') + .reply(200, 'logged out') + .get('/profile/adobe/blog/main') + .reply(401, '{ "status": 401 }', { 'content-type': 'application/json' }); + const test = new SidekickTest({ browser, page, plugin: 'user-logout', - sleep: 2000, + pluginSleep: 3000, checkPage: async (p) => p.evaluate(() => window.hlx.sidekick.config), }); test.sidekickConfig.authToken = 'foobar';