Skip to content

Commit

Permalink
fix: first login tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dylandepass committed Feb 16, 2024
1 parent 2ad3293 commit 39ff039
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 7 deletions.
1 change: 1 addition & 0 deletions src/extension/app/components/action-bar/picker/picker.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
*/

// @ts-nocheck
/* istanbul ignore file */

import { html } from '@spectrum-web-components/base';
import { Picker as SPPicker } from '@spectrum-web-components/picker';
Expand Down
14 changes: 11 additions & 3 deletions src/extension/app/components/plugin/login/login.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* governing permissions and limitations under the License.
*/

import { customElement } from 'lit/decorators.js';
import { customElement, property } from 'lit/decorators.js';
import { reaction } from 'mobx';
import { html, LitElement, css } from 'lit';
import { appStore } from '../../../store/app.js';
Expand All @@ -22,6 +22,13 @@ import { appStore } from '../../../store/app.js';
*/
@customElement('login-button')
export class LoginButton extends LitElement {
/**
* Are we ready to enable?
* @type {Boolean}
*/
@property({ type: Boolean })
accessor ready = false;

static styles = css`
sp-action-menu {
--mod-popover-content-area-spacing-vertical: 0;
Expand Down Expand Up @@ -49,6 +56,7 @@ export class LoginButton extends LitElement {
reaction(
() => appStore.status,
() => {
this.ready = true;
this.requestUpdate();
},
);
Expand All @@ -67,7 +75,7 @@ export class LoginButton extends LitElement {
return html`
${!appStore.status.profile || !appStore.isAuthenticated()
? html`
<sp-action-button quiet class="login" @click=${this.login}>Sign in</sp-action-button>
<sp-action-button quiet class="login" @click=${this.login} .disabled=${!this.ready}>${appStore.i18n('user_login')}</sp-action-button>
` : ''
}
${appStore.isAuthenticated()
Expand All @@ -79,7 +87,7 @@ export class LoginButton extends LitElement {
>
<sp-icon-real-time-customer-profile slot="icon"></sp-icon-real-time-customer-profile>
<sp-menu-item class="user" value="user">
${profile.picture ? `<img src=${profile.picture} slot="icon" alt=${profile.name} />` : html`<div class="no-picture" slot="icon"><sp-icon-user size="xl"></sp-icon-user></div>`}
${profile.picture ? html`<img src=${profile.picture} slot="icon" alt=${profile.name} />` : html`<div class="no-picture" slot="icon"><sp-icon-user size="xl"></sp-icon-user></div>`}
${profile.name}
<span slot="description">${profile.email}</span>
</sp-menu-item>
Expand Down
14 changes: 11 additions & 3 deletions src/extension/app/store/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,15 @@ export class AppStore {
}
}

/**
* Opens a new page. Abstracted for testing.
* @param {string} url The URL to open
* @returns {Window} The window object
*/
openPage(url) {
return window.open(url);
}

/**
* Fires an event with the given name.
* @param {string} name The name of the event
Expand Down Expand Up @@ -902,12 +911,11 @@ export class AppStore {
if (!extensionId || window.navigator.vendor.includes('Apple')) { // exclude safari
extensionId = 'cookie';
}
console.log('extensionId', extensionId);
loginUrl.searchParams.set('extensionId', extensionId);
if (selectAccount) {
loginUrl.searchParams.set('selectAccount', 'true');
}
const loginWindow = window.open(loginUrl.toString());
const loginWindow = this.openPage(loginUrl.toString());

let attempts = 0;

Expand Down Expand Up @@ -960,7 +968,7 @@ export class AppStore {
extensionId = 'cookie';
}
logoutUrl.searchParams.set('extensionId', extensionId);
const logoutWindow = window.open(logoutUrl.toString());
const logoutWindow = this.openPage(logoutUrl.toString());

let attempts = 0;

Expand Down
114 changes: 114 additions & 0 deletions test/app/components/plugin/login.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright 2023 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
/* eslint-disable no-unused-expressions, import/no-extraneous-dependencies */

// @ts-ignore
import fetchMock from 'fetch-mock/esm/client.js';
import sinon from 'sinon';
import { expect, waitUntil } from '@open-wc/testing';
import { recursiveQuery } from '../../../test-utils.js';
import chromeMock from '../../../mocks/chrome.js';
import { AEMSidekick } from '../../../../src/extension/app/aem-sidekick.js';
import { mockFetchEnglishMessagesSuccess } from '../../../mocks/i18n.js';
import { defaultSidekickConfig } from '../../../fixtures/sidekick-config.js';
import {
mockFetchConfigJSONNotFound,
mockFetchProfileSuccess,
mockFetchProfileUnauthorized,
mockFetchStatusSuccess,
mockFetchStatusWithProfileSuccess,
} from '../../../mocks/helix-admin.js';
import '../../../../src/extension/index.js';
import { appStore } from '../../../../src/extension/app/store/app.js';
import { mockHelixEnvironment, restoreEnvironment } from '../../../mocks/environment.js';
import { EventBus } from '../../../../src/extension/app/utils/event-bus.js';
import { EVENTS, MODALS } from '../../../../src/extension/app/constants.js';

// @ts-ignore
window.chrome = chromeMock;

describe('Login', () => {
let sidekick;
beforeEach(async () => {
mockFetchEnglishMessagesSuccess();
});

afterEach(() => {
document.body.removeChild(sidekick);
fetchMock.reset();
restoreEnvironment(document);
});

describe('Login', () => {
async function login() {
mockFetchStatusSuccess();
mockFetchConfigJSONNotFound();
mockFetchProfileSuccess();
mockHelixEnvironment(document, 'preview');

// @ts-ignore
const openStub = sinon.stub(appStore, 'openPage').returns({ closed: true });
const modalSpy = sinon.spy();
EventBus.instance.addEventListener(EVENTS.OPEN_MODAL, modalSpy);

sidekick = new AEMSidekick(defaultSidekickConfig);
document.body.appendChild(sidekick);

await waitUntil(() => recursiveQuery(sidekick, 'login-button'));
const loginButton = recursiveQuery(sidekick, 'login-button');

const loginActionButton = recursiveQuery(loginButton, 'sp-action-button');

await waitUntil(() => loginActionButton.getAttribute('disabled') === null);

expect(loginActionButton).to.exist;
expect(loginActionButton.textContent).to.eq('Sign in');

mockFetchStatusWithProfileSuccess();
loginActionButton.click();

await waitUntil(() => recursiveQuery(sidekick, 'dialog-view'));
await waitUntil(() => recursiveQuery(sidekick, 'dialog-view') === undefined);
await waitUntil(() => recursiveQuery(loginButton, 'sp-menu-item.user'));

expect(modalSpy.calledOnce).to.be.true;
expect(modalSpy.args[0][0].detail.type).to.equal(MODALS.WAIT);
expect(openStub.calledOnce).to.be.true;

openStub.restore();
}

it.skip('Successful login ', async () => {
await login();
}).timeout(20000);

it('Successful logout ', async () => {
await login();

// @ts-ignore
const openStub = sinon.stub(appStore, 'openPage').returns({ closed: true });
const accountElement = recursiveQuery(sidekick, 'login-button');
const accountButton = recursiveQuery(accountElement, 'sp-action-button');
accountButton.click();

const accountMenu = recursiveQuery(accountElement, 'sp-action-menu');
await waitUntil(() => accountMenu.getAttribute('open') !== null);

mockFetchProfileUnauthorized();
const logoutButton = recursiveQuery(accountMenu, 'sp-menu-item.logout');
logoutButton.click();
await waitUntil(() => recursiveQuery(sidekick, 'dialog-view'));
await waitUntil(() => recursiveQuery(sidekick, 'dialog-view') === undefined);
openStub.restore();
}).timeout(20000);
});
});
55 changes: 54 additions & 1 deletion test/fixtures/helix-admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ export const defaultGdriveStatusResponse = {
},
};

export const statusResponseWithProfile = {
export const defaultStatusResponseWithProfile = {
...defaultGdriveStatusResponse,
profile: {
iss: 'https://accounts.google.com',
Expand All @@ -242,6 +242,59 @@ export const statusResponseWithProfile = {
},
};

export const defaultGdriveProfileResponse = {
status: 200,
profile: {
iss: 'https://accounts.google.com',
aud: 'user-id.apps.googleusercontent.com',
sub: 'sub-id',
hd: 'example.com',
email: 'foo@example.com',
email_verified: true,
name: 'Dylan DePass',
picture: 'https://lh3.googleusercontent.com/a/ACg8ocLr6H7E_PkxRUeyyalOW2BYgEt7boQk7yzVCrVIm4g4=s96-c',
given_name: 'Peter',
family_name: 'Parker',
locale: 'en',
iat: 1708100467,
exp: 1708104067,
ttl: 3190,
hlx_hash: 'hash',
},
links: {
logout: 'https://admin.hlx.page/logout/adobe/aem-boilerplate/main',
},
};

export const defaultSharepointProfileResponse = {
status: 200,
profile: {
aud: 'aud-id',
iss: 'https://login.microsoftonline.com/fa7b1b5a-7b34-4387-94ae-d2c178decee1/v2.0',
iat: 1708104155,
nbf: 1708104155,
exp: 1708108055,
email: 'foo@example.com',
name: 'Peter Parker',
oid: 'oid-id',
preferred_username: 'foo@example.com',
rh: 'rh-id',
roles: [
'admin',
],
sub: 'sub-id',
tid: 'tid-id',
uti: 'uti-id',
ver: '2.0',
ttl: 3349,
hlx_hash: 'hash',
picture: 'https://admin.hlx.page/profile/adobe/aem-boilerplate/main/user-id/picture',
},
links: {
logout: 'https://admin.hlx.page/logout/adobe/aem-boilerplate/main',
},
};

export const defaultDirectorySharepointStatusResponse = {
webPath: '/',
resourcePath: '/',
Expand Down
19 changes: 19 additions & 0 deletions test/mocks/helix-admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import {
defaultSharepointStatusResponse,
defaultGdriveStatusResponse,
defaultDirectorySharepointStatusResponse,
defaultGdriveProfileResponse,
defaultSharepointProfileResponse,
defaultStatusResponseWithProfile,
} from '../fixtures/helix-admin.js';
import {
getDefaultEditorEnviromentLocations,
Expand Down Expand Up @@ -61,6 +64,22 @@ export const mockFetchStatusSuccess = (overrides = {}, contentSource = 'sharepoi
body: contentSource === 'sharepoint' ? { ...defaultSharepointStatusResponse, ...overrides } : { ...defaultGdriveStatusResponse, ...overrides },
}, { overwriteRoutes: true });

export const mockFetchStatusWithProfileSuccess = () => fetchMock.get(defaultStatusUrl, {
status: 200,
body: defaultStatusResponseWithProfile,
}, { overwriteRoutes: true });

export const defaultProfileUrl = 'https://admin.hlx.page/profile/adobe/aem-boilerplate/main';
export const mockFetchProfileSuccess = (overrides = {}, contentSource = 'sharepoint') => fetchMock.get(defaultProfileUrl, {
status: 200,
body: contentSource === 'sharepoint' ? { ...defaultSharepointProfileResponse, ...overrides } : { ...defaultGdriveProfileResponse, ...overrides },
}, { overwriteRoutes: true });

export const mockFetchProfileUnauthorized = () => fetchMock.get(defaultProfileUrl, {
status: 200,
body: { status: 401 },
}, { overwriteRoutes: true });

export const sharepointEditorDocStatusUrl = `https://admin.hlx.page/status/adobe/aem-boilerplate/main?editUrl=${encodeURIComponent(getDefaultEditorEnviromentLocations('sharepoint', 'doc'))}`;
export const mockSharepointEditorDocFetchStatusSuccess = (overrides = {}) => fetchMock.get(sharepointEditorDocStatusUrl, {
status: 200,
Expand Down

0 comments on commit 39ff039

Please sign in to comment.