test: add tests for git connection (#1289)
* chore: add data-test attributes * test: add github connection test, add applications modal * chore: embed test GITHUB_CLIENT_* environment values --------- Co-authored-by: Ali BARIN <ali.barin53@gmail.com>
This commit is contained in:
2
.github/workflows/playwright.yml
vendored
2
.github/workflows/playwright.yml
vendored
@@ -105,6 +105,8 @@ jobs:
|
|||||||
LOGIN_EMAIL: user@automatisch.io
|
LOGIN_EMAIL: user@automatisch.io
|
||||||
LOGIN_PASSWORD: sample
|
LOGIN_PASSWORD: sample
|
||||||
BASE_URL: http://localhost:3000
|
BASE_URL: http://localhost:3000
|
||||||
|
GITHUB_CLIENT_ID: 1c0417daf898adfbd99a
|
||||||
|
GITHUB_CLIENT_SECRET: 3328fa814dd582ccd03dbe785cfd683fb8da92b3
|
||||||
run: yarn test
|
run: yarn test
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
if: always()
|
if: always()
|
||||||
|
34
packages/e2e-tests/fixtures/applications-modal.js
Normal file
34
packages/e2e-tests/fixtures/applications-modal.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
const { GithubPage } = require('./apps/github/github-page');
|
||||||
|
const { BasePage } = require('./base-page');
|
||||||
|
|
||||||
|
export class ApplicationsModal extends BasePage {
|
||||||
|
|
||||||
|
applications = {
|
||||||
|
github: GithubPage
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('@playwright/test').Page} page
|
||||||
|
*/
|
||||||
|
constructor (page) {
|
||||||
|
super(page);
|
||||||
|
this.modal = page.getByTestId('add-app-connection-dialog');
|
||||||
|
this.searchInput = page.getByTestId('search-for-app-text-field');
|
||||||
|
this.appListItem = page.getByTestId('app-list-item');
|
||||||
|
this.appLoader = page.getByTestId('search-for-app-loader');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string link
|
||||||
|
*/
|
||||||
|
async selectLink (link) {
|
||||||
|
if (this.applications[link] === undefined) {
|
||||||
|
throw {
|
||||||
|
message: `Unknown link "${link}" passed to ApplicationsModal.selectLink`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await this.searchInput.fill(link);
|
||||||
|
await this.appListItem.first().click();
|
||||||
|
return new this.applications[link](this.page);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,5 @@
|
|||||||
const path = require('node:path');
|
const path = require('node:path');
|
||||||
|
const { ApplicationsModal } = require('./applications-modal');
|
||||||
const { AuthenticatedPage } = require('./authenticated-page');
|
const { AuthenticatedPage } = require('./authenticated-page');
|
||||||
|
|
||||||
export class ApplicationsPage extends AuthenticatedPage {
|
export class ApplicationsPage extends AuthenticatedPage {
|
||||||
@@ -13,4 +14,9 @@ export class ApplicationsPage extends AuthenticatedPage {
|
|||||||
this.drawerLink = this.page.getByTestId('apps-page-drawer-link');
|
this.drawerLink = this.page.getByTestId('apps-page-drawer-link');
|
||||||
this.addConnectionButton = this.page.getByTestId('add-connection-button');
|
this.addConnectionButton = this.page.getByTestId('add-connection-button');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async openAddConnectionModal () {
|
||||||
|
await this.addConnectionButton.click();
|
||||||
|
return new ApplicationsModal(this.page);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,49 @@
|
|||||||
|
import { GithubPopup } from './github-popup';
|
||||||
|
|
||||||
|
const { BasePage } = require('../../base-page');
|
||||||
|
|
||||||
|
export class AddGithubConnectionModal extends BasePage {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('@playwright/test').Page} page
|
||||||
|
*/
|
||||||
|
constructor (page) {
|
||||||
|
super(page);
|
||||||
|
this.modal = page.getByTestId('add-app-connection-dialog');
|
||||||
|
this.oauthRedirectInput = page.getByTestId('oAuthRedirectUrl-text');
|
||||||
|
this.clientIdInput = page.getByTestId('consumerKey-text');
|
||||||
|
this.clientIdSecretInput = page.getByTestId('consumerSecret-text');
|
||||||
|
this.submitButton = page.getByTestId('create-connection-button');
|
||||||
|
}
|
||||||
|
|
||||||
|
async visible () {
|
||||||
|
return await this.modal.isVisible();
|
||||||
|
}
|
||||||
|
|
||||||
|
async inputForm () {
|
||||||
|
await connectionModal.clientIdInput.fill(
|
||||||
|
process.env.GITHUB_CLIENT_ID
|
||||||
|
);
|
||||||
|
await connectionModal.clientIdSecretInput.fill(
|
||||||
|
process.env.GITHUB_CLIENT_SECRET
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {import('@playwright/test').Page}
|
||||||
|
*/
|
||||||
|
async submit () {
|
||||||
|
const popupPromise = this.page.waitForEvent('popup');
|
||||||
|
await this.submitButton.click();
|
||||||
|
const popup = await popupPromise;
|
||||||
|
await popup.bringToFront();
|
||||||
|
return popup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('@playwright/test').Page} page
|
||||||
|
*/
|
||||||
|
async handlePopup (page) {
|
||||||
|
return await GithubPopup.handle(page);
|
||||||
|
}
|
||||||
|
}
|
65
packages/e2e-tests/fixtures/apps/github/github-page.js
Normal file
65
packages/e2e-tests/fixtures/apps/github/github-page.js
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
const { BasePage } = require('../../base-page');
|
||||||
|
const { AddGithubConnectionModal } = require('./add-github-connection-modal');
|
||||||
|
|
||||||
|
export class GithubPage extends BasePage {
|
||||||
|
|
||||||
|
constructor (page) {
|
||||||
|
super(page)
|
||||||
|
this.addConnectionButton = page.getByTestId('add-connection-button');
|
||||||
|
this.connectionsTab = page.getByTestId('connections-tab');
|
||||||
|
this.flowsTab = page.getByTestId('flows-tab');
|
||||||
|
this.connectionRows = page.getByTestId('connection-row');
|
||||||
|
this.flowRows = page.getByTestId('flow-row');
|
||||||
|
this.firstConnectionButton = page.getByTestId('connections-no-results');
|
||||||
|
this.firstFlowButton = page.getByTestId('flows-no-results');
|
||||||
|
this.addConnectionModal = new AddGithubConnectionModal(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
async goto () {
|
||||||
|
await this.page.goto('/app/github/connections');
|
||||||
|
}
|
||||||
|
|
||||||
|
async openConnectionModal () {
|
||||||
|
await this.addConnectionButton.click();
|
||||||
|
await expect(this.addConnectionButton.modal).toBeVisible();
|
||||||
|
return this.addConnectionModal;
|
||||||
|
}
|
||||||
|
|
||||||
|
async flowsVisible () {
|
||||||
|
return this.page.url() === await this.flowsTab.getAttribute('href');
|
||||||
|
}
|
||||||
|
|
||||||
|
async connectionsVisible () {
|
||||||
|
return this.page.url() === await this.connectionsTab.getAttribute('href');
|
||||||
|
}
|
||||||
|
|
||||||
|
async hasFlows () {
|
||||||
|
if (!(await this.flowsVisible())) {
|
||||||
|
await this.flowsTab.click();
|
||||||
|
await expect(this.flowsTab).toBeVisible();
|
||||||
|
}
|
||||||
|
return await this.flowRows.count() > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
async hasConnections () {
|
||||||
|
if (!(await this.connectionsVisible())) {
|
||||||
|
await this.connectionsTab.click();
|
||||||
|
await expect(this.connectionsTab).toBeVisible();
|
||||||
|
}
|
||||||
|
return await this.connectionRows.count() > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import('@playwright/test').Page} page
|
||||||
|
*/
|
||||||
|
export async function initGithubConnection (page) {
|
||||||
|
// assumes already logged in
|
||||||
|
const githubPage = new GithubPage(page);
|
||||||
|
await githubPage.goto();
|
||||||
|
const modal = await githubPage.openConnectionModal();
|
||||||
|
await modal.inputForm();
|
||||||
|
const popup = await modal.submit();
|
||||||
|
await modal.handlePopup(popup);
|
||||||
|
}
|
92
packages/e2e-tests/fixtures/apps/github/github-popup.js
Normal file
92
packages/e2e-tests/fixtures/apps/github/github-popup.js
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
const { BasePage } = require('../../base-page');
|
||||||
|
|
||||||
|
export class GithubPopup extends BasePage {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('@playwright/test').Page} page
|
||||||
|
*/
|
||||||
|
static async handle (page) {
|
||||||
|
const popup = new GithubPopup(page);
|
||||||
|
return await popup.handleAuthFlow();
|
||||||
|
}
|
||||||
|
|
||||||
|
getPathname () {
|
||||||
|
const url = this.page.url()
|
||||||
|
try {
|
||||||
|
return new URL(url).pathname;
|
||||||
|
} catch (e) {
|
||||||
|
return new URL(`https://github.com/${url}`).pathname;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleAuthFlow () {
|
||||||
|
if (this.getPathname() === '/login') {
|
||||||
|
await this.handleLogin();
|
||||||
|
}
|
||||||
|
if (this.page.isClosed()) { return; }
|
||||||
|
if (this.getPathname() === '/login/oauth/authorize') {
|
||||||
|
await this.handleAuthorize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleLogin () {
|
||||||
|
const loginInput = this.page.getByLabel('Username or email address');
|
||||||
|
loginInput.click();
|
||||||
|
await loginInput.fill(process.env.GITHUB_USERNAME);
|
||||||
|
const passwordInput = this.page.getByLabel('Password');
|
||||||
|
passwordInput.click()
|
||||||
|
await passwordInput.fill(process.env.GITHUB_PASSWORD);
|
||||||
|
await this.page.getByRole('button', { name: 'Sign in' }).click();
|
||||||
|
// await this.page.waitForTimeout(2000);
|
||||||
|
if (this.page.isClosed()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// await this.page.waitForLoadState('networkidle', 30000);
|
||||||
|
this.page.waitForEvent('load');
|
||||||
|
if (this.page.isClosed()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await this.page.waitForURL(function (url) {
|
||||||
|
const u = new URL(url);
|
||||||
|
return (
|
||||||
|
u.pathname === '/login/oauth/authorize'
|
||||||
|
) && u.searchParams.get('client_id');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleAuthorize () {
|
||||||
|
if (this.page.isClosed()) { return }
|
||||||
|
const authorizeButton = this.page.getByRole(
|
||||||
|
'button',
|
||||||
|
{ name: 'Authorize' }
|
||||||
|
);
|
||||||
|
await this.page.waitForEvent('load');
|
||||||
|
await authorizeButton.click();
|
||||||
|
await this.page.waitForURL(function (url) {
|
||||||
|
const u = new URL(url);
|
||||||
|
return (
|
||||||
|
u.pathname === '/login/oauth/authorize'
|
||||||
|
) && (
|
||||||
|
u.searchParams.get('client_id') === null
|
||||||
|
);
|
||||||
|
})
|
||||||
|
const passwordInput = this.page.getByLabel('Password');
|
||||||
|
if (await passwordInput.isVisible()) {
|
||||||
|
await passwordInput.fill(process.env.GITHUB_PASSWORD);
|
||||||
|
const submitButton = this.page
|
||||||
|
.getByRole('button')
|
||||||
|
.filter({ hasText: /confirm|submit|enter|go|sign in/gmi });
|
||||||
|
if (await submitButton.isVisible()) {
|
||||||
|
submitButton.waitFor();
|
||||||
|
await expect(submitButton).toBeEnabled();
|
||||||
|
await submitButton.click();
|
||||||
|
} else {
|
||||||
|
throw {
|
||||||
|
page: this.page,
|
||||||
|
error: 'Could not find submit button for confirming user account'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await this.page.waitForEvent('close')
|
||||||
|
}
|
||||||
|
}
|
63
packages/e2e-tests/tests/app-integrations/github.spec.js
Normal file
63
packages/e2e-tests/tests/app-integrations/github.spec.js
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
const { test, expect } = require('../../fixtures');
|
||||||
|
|
||||||
|
test('Github OAuth integration', async ({ page, applicationsPage }) => {
|
||||||
|
const githubConnectionPage = await test.step(
|
||||||
|
'Navigate to github connections modal',
|
||||||
|
async () => {
|
||||||
|
await applicationsPage.drawerLink.click();
|
||||||
|
if (page.url() !== '/apps') {
|
||||||
|
await page.waitForURL('/apps');
|
||||||
|
}
|
||||||
|
const connectionModal = await applicationsPage.openAddConnectionModal();
|
||||||
|
expect(connectionModal.modal).toBeVisible();
|
||||||
|
return await connectionModal.selectLink('github');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const connectionModal = await test.step(
|
||||||
|
'Ensure the github connection modal is visible',
|
||||||
|
async () => {
|
||||||
|
const connectionModal = githubConnectionPage.addConnectionModal;
|
||||||
|
expect(connectionModal.modal).toBeVisible();
|
||||||
|
return connectionModal;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const githubPopup = await test.step(
|
||||||
|
'Input data into the add connection form and submit',
|
||||||
|
async () => {
|
||||||
|
await connectionModal.clientIdInput.fill(process.env.GITHUB_CLIENT_ID);
|
||||||
|
await connectionModal.clientIdSecretInput.fill(
|
||||||
|
process.env.GITHUB_CLIENT_SECRET
|
||||||
|
);
|
||||||
|
return await connectionModal.submit();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await test.step('Ensure github popup is not a 404', async () => {
|
||||||
|
// expect(githubPopup).toBeVisible();
|
||||||
|
const title = await githubPopup.title();
|
||||||
|
expect(title).not.toMatch(/^Page not found/);
|
||||||
|
});
|
||||||
|
|
||||||
|
/* Skip these in CI
|
||||||
|
await test.step(
|
||||||
|
'Handle github popup authentication flow',
|
||||||
|
async () => {
|
||||||
|
await connectionModal.handlePopup(githubPopup);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
await test.step(
|
||||||
|
'Ensure the new connection is added to the connections list',
|
||||||
|
async () => {
|
||||||
|
await page.locator('body').click({ position: { x: 0, y: 0 } });
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
});
|
||||||
|
|
||||||
|
test.afterAll(async () => {
|
||||||
|
// TODO - Remove connections from github connections page
|
||||||
|
});
|
@@ -29,6 +29,7 @@ export default function AppConnections(
|
|||||||
<NoResultFound
|
<NoResultFound
|
||||||
to={URLS.APP_ADD_CONNECTION(appKey)}
|
to={URLS.APP_ADD_CONNECTION(appKey)}
|
||||||
text={formatMessage('app.noConnections')}
|
text={formatMessage('app.noConnections')}
|
||||||
|
data-test="connections-no-results"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -45,6 +45,7 @@ export default function AppFlows(props: AppFlowsProps): React.ReactElement {
|
|||||||
<NoResultFound
|
<NoResultFound
|
||||||
to={URLS.CREATE_FLOW_WITH_APP_AND_CONNECTION(appKey, connectionId)}
|
to={URLS.CREATE_FLOW_WITH_APP_AND_CONNECTION(appKey, connectionId)}
|
||||||
text={formatMessage('app.noFlows')}
|
text={formatMessage('app.noFlows')}
|
||||||
|
data-test="flows-no-results"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -190,6 +190,7 @@ export default function InputCreator(
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onBlur={onBlur}
|
onBlur={onBlur}
|
||||||
name={computedName}
|
name={computedName}
|
||||||
|
data-test={`${computedName}-text`}
|
||||||
label={label}
|
label={label}
|
||||||
fullWidth
|
fullWidth
|
||||||
helperText={description}
|
helperText={description}
|
||||||
|
@@ -175,6 +175,7 @@ export default function Application(): React.ReactElement | null {
|
|||||||
value={URLS.APP_CONNECTIONS_PATTERN}
|
value={URLS.APP_CONNECTIONS_PATTERN}
|
||||||
disabled={!app.supportsConnections}
|
disabled={!app.supportsConnections}
|
||||||
component={Link}
|
component={Link}
|
||||||
|
data-test="connections-tab"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Tab
|
<Tab
|
||||||
@@ -182,6 +183,7 @@ export default function Application(): React.ReactElement | null {
|
|||||||
to={URLS.APP_FLOWS(appKey)}
|
to={URLS.APP_FLOWS(appKey)}
|
||||||
value={URLS.APP_FLOWS_PATTERN}
|
value={URLS.APP_FLOWS_PATTERN}
|
||||||
component={Link}
|
component={Link}
|
||||||
|
data-test="flows-tab"
|
||||||
/>
|
/>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Box>
|
</Box>
|
||||||
|
Reference in New Issue
Block a user