Compare commits
5 Commits
AUT-962
...
migrate-fl
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9752e2c4d2 | ||
![]() |
a5b31da3cc | ||
![]() |
8f7785e9d2 | ||
![]() |
69297c2dd8 | ||
![]() |
1c8e9fac7c |
25
.github/workflows/playwright.yml
vendored
Normal file
25
.github/workflows/playwright.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
name: Automatisch UI Test
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 12 * * *'
|
||||
jobs:
|
||||
test:
|
||||
timeout-minutes: 60
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
- name: Install Playwright Browsers
|
||||
run: yarn playwright install --with-deps
|
||||
- name: Run Playwright tests
|
||||
run: yarn playwright test
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: playwright-report/
|
||||
retention-days: 30
|
5
packages/e2e-tests/.gitignore
vendored
Normal file
5
packages/e2e-tests/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
node_modules/
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/playwright/.cache/
|
||||
/output
|
12
packages/e2e-tests/fixtures/applications-page.js
Normal file
12
packages/e2e-tests/fixtures/applications-page.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const path = require('node:path');
|
||||
const { BasePage } = require('./base-page');
|
||||
|
||||
export class ApplicationsPage extends BasePage {
|
||||
async screenshot(options = {}) {
|
||||
const { path: plainPath, ...restOptions } = options;
|
||||
|
||||
const computedPath = path.join('applications', plainPath);
|
||||
|
||||
return await super.screenshot({ path: computedPath, ...restOptions });
|
||||
}
|
||||
}
|
34
packages/e2e-tests/fixtures/base-page.js
Normal file
34
packages/e2e-tests/fixtures/base-page.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const path = require('node:path');
|
||||
|
||||
export class BasePage {
|
||||
/**
|
||||
* @param {import('@playwright/test').Page} page
|
||||
*/
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
async clickAway() {
|
||||
await this.page.locator('body').click({ position: { x: 0, y: 0 } });
|
||||
}
|
||||
|
||||
async screenshot(options = {}) {
|
||||
const { path: plainPath, ...restOptions } = options;
|
||||
|
||||
const computedPath = path.join('output/screenshots', plainPath);
|
||||
|
||||
return await this.page.screenshot({ path: computedPath, ...restOptions });
|
||||
}
|
||||
|
||||
async login() {
|
||||
await this.page.goto('/login');
|
||||
await this.page
|
||||
.getByTestId('email-text-field')
|
||||
.fill(process.env.LOGIN_EMAIL);
|
||||
await this.page
|
||||
.getByTestId('password-text-field')
|
||||
.fill(process.env.LOGIN_PASSWORD);
|
||||
|
||||
await this.page.getByTestId('login-button').click();
|
||||
}
|
||||
}
|
16
packages/e2e-tests/fixtures/connections-page.js
Normal file
16
packages/e2e-tests/fixtures/connections-page.js
Normal file
@@ -0,0 +1,16 @@
|
||||
const path = require('node:path');
|
||||
const { BasePage } = require('./base-page');
|
||||
|
||||
export class ConnectionsPage extends BasePage {
|
||||
async screenshot(options = {}) {
|
||||
const { path: plainPath, ...restOptions } = options;
|
||||
|
||||
const computedPath = path.join('connections', plainPath);
|
||||
|
||||
return await super.screenshot({ path: computedPath, ...restOptions });
|
||||
}
|
||||
|
||||
async clickAddConnectionButton() {
|
||||
await this.page.getByTestId('add-connection-button').click();
|
||||
}
|
||||
}
|
12
packages/e2e-tests/fixtures/executions-page.js
Normal file
12
packages/e2e-tests/fixtures/executions-page.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const path = require('node:path');
|
||||
const { BasePage } = require('./base-page');
|
||||
|
||||
export class ExecutionsPage extends BasePage {
|
||||
async screenshot(options = {}) {
|
||||
const { path: plainPath, ...restOptions } = options;
|
||||
|
||||
const computedPath = path.join('executions', plainPath);
|
||||
|
||||
return await super.screenshot({ path: computedPath, ...restOptions });
|
||||
}
|
||||
}
|
27
packages/e2e-tests/fixtures/flow-editor-page.js
Normal file
27
packages/e2e-tests/fixtures/flow-editor-page.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const path = require('node:path');
|
||||
const { BasePage } = require('./base-page');
|
||||
|
||||
export class FlowEditorPage extends BasePage {
|
||||
constructor(page) {
|
||||
super(page);
|
||||
this.appAutocomplete = this.page.getByTestId('choose-app-autocomplete');
|
||||
this.eventAutocomplete = this.page.getByTestId('choose-event-autocomplete');
|
||||
this.continueButton = this.page.getByTestId('flow-substep-continue-button');
|
||||
this.connectionAutocomplete = this.page.getByTestId(
|
||||
'choose-connection-autocomplete'
|
||||
);
|
||||
this.testOuput = this.page.getByTestId('flow-test-substep-output');
|
||||
this.unpublishFlowButton = this.page.getByTestId('unpublish-flow-button');
|
||||
this.publishFlowButton = this.page.getByTestId('publish-flow-button');
|
||||
this.infoSnackbar = this.page.getByTestId('flow-cannot-edit-info-snackbar');
|
||||
this.trigger = this.page.getByLabel('Trigger on weekends?');
|
||||
}
|
||||
|
||||
async screenshot(options = {}) {
|
||||
const { path: plainPath, ...restOptions } = options;
|
||||
|
||||
const computedPath = path.join('flow-editor', plainPath);
|
||||
|
||||
return await super.screenshot({ path: computedPath, ...restOptions });
|
||||
}
|
||||
}
|
21
packages/e2e-tests/fixtures/index.js
Normal file
21
packages/e2e-tests/fixtures/index.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const base = require('@playwright/test');
|
||||
const { ApplicationsPage } = require('./applications-page');
|
||||
const { ConnectionsPage } = require('./connections-page');
|
||||
const { ExecutionsPage } = require('./executions-page');
|
||||
const { FlowEditorPage } = require('./flow-editor-page');
|
||||
|
||||
exports.test = base.test.extend({
|
||||
applicationsPage: async ({ page }, use) => {
|
||||
await use(new ApplicationsPage(page));
|
||||
},
|
||||
connectionsPage: async ({ page }, use) => {
|
||||
await use(new ConnectionsPage(page));
|
||||
},
|
||||
executionsPage: async ({ page }, use) => {
|
||||
await use(new ExecutionsPage(page));
|
||||
},
|
||||
flowEditorPage: async ({ page }, use) => {
|
||||
await use(new FlowEditorPage(page));
|
||||
},
|
||||
});
|
||||
exports.expect = base.expect;
|
@@ -5,7 +5,8 @@
|
||||
"private": true,
|
||||
"description": "The open source Zapier alternative. Build workflow automation without spending time and money.",
|
||||
"scripts": {
|
||||
"open": "cypress open"
|
||||
"open": "cypress open",
|
||||
"playwright": "playwright test"
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
@@ -22,6 +23,10 @@
|
||||
"url": "https://github.com/automatisch/automatisch/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.36.2",
|
||||
"cypress": "^10.9.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"dotenv": "^16.3.1"
|
||||
}
|
||||
}
|
||||
|
82
packages/e2e-tests/playwright.config.js
Normal file
82
packages/e2e-tests/playwright.config.js
Normal file
@@ -0,0 +1,82 @@
|
||||
// @ts-check
|
||||
const { defineConfig, devices } = require('@playwright/test');
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
* https://github.com/motdotla/dotenv
|
||||
*/
|
||||
require('dotenv').config();
|
||||
|
||||
/**
|
||||
* @see https://playwright.dev/docs/test-configuration
|
||||
*/
|
||||
module.exports = defineConfig({
|
||||
testDir: './tests',
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: true,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !!process.env.CI,
|
||||
/* Retry on CI only */
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
/* Opt out of parallel tests on CI. */
|
||||
workers: process.env.CI ? 1 : undefined,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: process.env.CI ? 'github' : 'html',
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
baseURL: process.env.CI
|
||||
? 'https://sandbox.automatisch.io'
|
||||
: 'http://localhost:3001',
|
||||
|
||||
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||
trace: 'on-first-retry',
|
||||
testIdAttribute: 'data-test',
|
||||
viewport: { width: 1280, height: 720 },
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
},
|
||||
|
||||
// {
|
||||
// name: 'firefox',
|
||||
// use: { ...devices['Desktop Firefox'] },
|
||||
// },
|
||||
|
||||
// {
|
||||
// name: 'webkit',
|
||||
// use: { ...devices['Desktop Safari'] },
|
||||
// },
|
||||
|
||||
/* Test against mobile viewports. */
|
||||
// {
|
||||
// name: 'Mobile Chrome',
|
||||
// use: { ...devices['Pixel 5'] },
|
||||
// },
|
||||
// {
|
||||
// name: 'Mobile Safari',
|
||||
// use: { ...devices['iPhone 12'] },
|
||||
// },
|
||||
|
||||
/* Test against branded browsers. */
|
||||
// {
|
||||
// name: 'Microsoft Edge',
|
||||
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
||||
// },
|
||||
// {
|
||||
// name: 'Google Chrome',
|
||||
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
|
||||
// },
|
||||
],
|
||||
|
||||
/* Run your local dev server before starting the tests */
|
||||
// webServer: {
|
||||
// command: 'npm run start',
|
||||
// url: 'http://127.0.0.1:3000',
|
||||
// reuseExistingServer: !process.env.CI,
|
||||
// },
|
||||
});
|
67
packages/e2e-tests/tests/apps/list-apps.spec.js
Normal file
67
packages/e2e-tests/tests/apps/list-apps.spec.js
Normal file
@@ -0,0 +1,67 @@
|
||||
// @ts-check
|
||||
const { test, expect } = require('../../fixtures/index');
|
||||
|
||||
test.describe('Apps page', () => {
|
||||
test.beforeEach(async ({ page, applicationsPage }) => {
|
||||
await applicationsPage.login();
|
||||
await page.getByTestId('apps-page-drawer-link').click();
|
||||
});
|
||||
|
||||
test('displays applications', async ({ page, applicationsPage }) => {
|
||||
await page.getByTestId('apps-loader').waitFor({
|
||||
state: 'detached',
|
||||
});
|
||||
await expect(page.getByTestId('app-row')).not.toHaveCount(0);
|
||||
|
||||
await applicationsPage.screenshot({
|
||||
path: 'Applications.png',
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('can add connection', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await expect(page.getByTestId('add-connection-button')).toBeVisible();
|
||||
await page.getByTestId('add-connection-button').click();
|
||||
await page
|
||||
.getByTestId('search-for-app-loader')
|
||||
.waitFor({ state: 'detached' });
|
||||
});
|
||||
|
||||
test('lists applications', async ({ page, applicationsPage }) => {
|
||||
const appListItemCount = await page.getByTestId('app-list-item').count();
|
||||
expect(appListItemCount).toBeGreaterThan(10);
|
||||
|
||||
await applicationsPage.clickAway();
|
||||
});
|
||||
|
||||
test('searches an application', async ({ page, applicationsPage }) => {
|
||||
await page.getByTestId('search-for-app-text-field').fill('DeepL');
|
||||
await expect(page.getByTestId('app-list-item')).toHaveCount(1);
|
||||
|
||||
await applicationsPage.clickAway();
|
||||
});
|
||||
|
||||
test('goes to app page to create a connection', async ({
|
||||
page,
|
||||
applicationsPage,
|
||||
}) => {
|
||||
await page.getByTestId('app-list-item').first().click();
|
||||
await expect(page).toHaveURL('/app/deepl/connections/add');
|
||||
await expect(page.getByTestId('add-app-connection-dialog')).toBeVisible();
|
||||
|
||||
await applicationsPage.clickAway();
|
||||
});
|
||||
|
||||
test('closes the dialog on backdrop click', async ({
|
||||
page,
|
||||
applicationsPage,
|
||||
}) => {
|
||||
await page.getByTestId('app-list-item').first().click();
|
||||
await expect(page).toHaveURL('/app/deepl/connections/add');
|
||||
await expect(page.getByTestId('add-app-connection-dialog')).toBeVisible();
|
||||
await applicationsPage.clickAway();
|
||||
await expect(page).toHaveURL('/app/deepl/connections');
|
||||
await expect(page.getByTestId('add-app-connection-dialog')).toBeHidden();
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,50 @@
|
||||
// @ts-check
|
||||
const { test, expect } = require('../../fixtures/index');
|
||||
|
||||
test.describe('Connections page', () => {
|
||||
test.beforeEach(async ({ page, connectionsPage }) => {
|
||||
await connectionsPage.login();
|
||||
await page.getByTestId('apps-page-drawer-link').click();
|
||||
await page.goto('/app/ntfy/connections');
|
||||
});
|
||||
|
||||
test('shows connections if any', async ({ page, connectionsPage }) => {
|
||||
await page.getByTestId('apps-loader').waitFor({
|
||||
state: 'detached',
|
||||
});
|
||||
|
||||
await connectionsPage.screenshot({
|
||||
path: 'Connections.png',
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('can add connection', () => {
|
||||
test('has a button to open add connection dialog', async ({ page }) => {
|
||||
await expect(page.getByTestId('add-connection-button')).toBeVisible();
|
||||
});
|
||||
|
||||
test('add connection button takes user to add connection page', async ({
|
||||
page,
|
||||
connectionsPage,
|
||||
}) => {
|
||||
await connectionsPage.clickAddConnectionButton();
|
||||
await expect(page).toHaveURL('/app/ntfy/connections/add');
|
||||
});
|
||||
|
||||
test('shows add connection dialog to create a new connection', async ({
|
||||
page,
|
||||
connectionsPage,
|
||||
}) => {
|
||||
await connectionsPage.clickAddConnectionButton();
|
||||
await expect(page).toHaveURL('/app/ntfy/connections/add');
|
||||
await page.getByTestId('create-connection-button').click();
|
||||
await expect(
|
||||
page.getByTestId('create-connection-button')
|
||||
).not.toBeVisible();
|
||||
|
||||
await connectionsPage.screenshot({
|
||||
path: 'Ntfy connections after creating a connection.png',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,39 @@
|
||||
// @ts-check
|
||||
const { test, expect } = require('../../fixtures/index');
|
||||
|
||||
test.describe('Executions page', () => {
|
||||
test.beforeEach(async ({ page, executionsPage }) => {
|
||||
await executionsPage.login();
|
||||
|
||||
await page.getByTestId('executions-page-drawer-link').click();
|
||||
await page.getByTestId('execution-row').first().click();
|
||||
|
||||
await expect(page).toHaveURL(/\/executions\//);
|
||||
});
|
||||
|
||||
test('displays data in by default', async ({ page, executionsPage }) => {
|
||||
await expect(page.getByTestId('execution-step').last()).toBeVisible();
|
||||
await expect(page.getByTestId('execution-step')).toHaveCount(2);
|
||||
|
||||
await executionsPage.screenshot({
|
||||
path: 'Execution - data in.png',
|
||||
});
|
||||
});
|
||||
|
||||
test('displays data out', async ({ page, executionsPage }) => {
|
||||
const executionStepCount = await page.getByTestId('execution-step').count();
|
||||
for (let i = 0; i < executionStepCount; i++) {
|
||||
await page.getByTestId('data-out-tab').nth(i).click();
|
||||
await expect(page.getByTestId('data-out-panel').nth(i)).toBeVisible();
|
||||
|
||||
await executionsPage.screenshot({
|
||||
path: `Execution - data out - ${i}.png`,
|
||||
animations: 'disabled',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test('does not display error', async ({ page }) => {
|
||||
await expect(page.getByTestId('error-tab')).toBeHidden();
|
||||
});
|
||||
});
|
19
packages/e2e-tests/tests/executions/list-executions.spec.js
Normal file
19
packages/e2e-tests/tests/executions/list-executions.spec.js
Normal file
@@ -0,0 +1,19 @@
|
||||
// @ts-check
|
||||
const { test, expect } = require('../../fixtures/index');
|
||||
|
||||
test.describe('Executions page', () => {
|
||||
test.beforeEach(async ({ page, executionsPage }) => {
|
||||
await executionsPage.login();
|
||||
|
||||
await page.getByTestId('executions-page-drawer-link').click();
|
||||
});
|
||||
|
||||
test('displays executions', async ({ page, executionsPage }) => {
|
||||
await page.getByTestId('executions-loader').waitFor({
|
||||
state: 'detached',
|
||||
});
|
||||
await expect(page.getByTestId('execution-row').first()).toBeVisible();
|
||||
|
||||
await executionsPage.screenshot({ path: 'Executions.png' });
|
||||
});
|
||||
});
|
205
packages/e2e-tests/tests/flow-editor/create-flow.spec.js
Normal file
205
packages/e2e-tests/tests/flow-editor/create-flow.spec.js
Normal file
@@ -0,0 +1,205 @@
|
||||
// @ts-check
|
||||
const { FlowEditorPage } = require('../../fixtures/flow-editor-page');
|
||||
const { test, expect } = require('../../fixtures/index');
|
||||
|
||||
test.describe.configure({ mode: 'serial' });
|
||||
|
||||
let page;
|
||||
let flowEditorPage;
|
||||
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
page = await browser.newPage();
|
||||
flowEditorPage = new FlowEditorPage(page);
|
||||
});
|
||||
|
||||
test('create flow', async ({}) => {
|
||||
await flowEditorPage.login();
|
||||
|
||||
await flowEditorPage.page.getByTestId('create-flow-button').click();
|
||||
await expect(flowEditorPage.page).toHaveURL(/\/editor\/create/);
|
||||
await expect(flowEditorPage.page).toHaveURL(
|
||||
/\/editor\/[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}/
|
||||
);
|
||||
});
|
||||
|
||||
test('has two steps by default', async ({}) => {
|
||||
await expect(flowEditorPage.page.getByTestId('flow-step')).toHaveCount(2);
|
||||
});
|
||||
|
||||
test.describe('arrange Scheduler trigger', () => {
|
||||
test.describe('choose app and event substep', () => {
|
||||
test('choose application', async ({}) => {
|
||||
await flowEditorPage.appAutocomplete.click();
|
||||
await flowEditorPage.page
|
||||
.getByRole('option', { name: 'Scheduler' })
|
||||
.click();
|
||||
});
|
||||
|
||||
test('choose an event', async ({}) => {
|
||||
await expect(flowEditorPage.eventAutocomplete).toBeVisible();
|
||||
await flowEditorPage.eventAutocomplete.click();
|
||||
await flowEditorPage.page
|
||||
.getByRole('option', { name: 'Every hour' })
|
||||
.click();
|
||||
});
|
||||
|
||||
test('continue to next step', async ({}) => {
|
||||
await flowEditorPage.continueButton.click();
|
||||
});
|
||||
|
||||
test('collapses the substep', async ({}) => {
|
||||
await expect(flowEditorPage.appAutocomplete).not.toBeVisible();
|
||||
await expect(flowEditorPage.eventAutocomplete).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('set up a trigger', () => {
|
||||
test('choose "yes" in "trigger on weekends?"', async ({}) => {
|
||||
await expect(flowEditorPage.trigger).toBeVisible();
|
||||
await flowEditorPage.trigger.click();
|
||||
await flowEditorPage.page.getByRole('option', { name: 'Yes' }).click();
|
||||
});
|
||||
|
||||
test('continue to next step', async ({}) => {
|
||||
await flowEditorPage.continueButton.click();
|
||||
});
|
||||
|
||||
test('collapses the substep', async ({}) => {
|
||||
await expect(flowEditorPage.trigger).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('test trigger', () => {
|
||||
test('show sample output', async ({}) => {
|
||||
await expect(flowEditorPage.testOuput).not.toBeVisible();
|
||||
await flowEditorPage.continueButton.click();
|
||||
await expect(flowEditorPage.testOuput).toBeVisible();
|
||||
await flowEditorPage.screenshot({
|
||||
path: 'Scheduler trigger test output.png',
|
||||
});
|
||||
await flowEditorPage.continueButton.click();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('arrange Ntfy action', () => {
|
||||
test.describe('choose app and event substep', () => {
|
||||
test('choose application', async ({}) => {
|
||||
await flowEditorPage.appAutocomplete.click();
|
||||
await flowEditorPage.page.getByRole('option', { name: 'Ntfy' }).click();
|
||||
});
|
||||
|
||||
test('choose an event', async ({}) => {
|
||||
await expect(flowEditorPage.eventAutocomplete).toBeVisible();
|
||||
await flowEditorPage.eventAutocomplete.click();
|
||||
await flowEditorPage.page
|
||||
.getByRole('option', { name: 'Send message' })
|
||||
.click();
|
||||
});
|
||||
|
||||
test('continue to next step', async ({}) => {
|
||||
await flowEditorPage.continueButton.click();
|
||||
});
|
||||
|
||||
test('collapses the substep', async ({}) => {
|
||||
await expect(flowEditorPage.appAutocomplete).not.toBeVisible();
|
||||
await expect(flowEditorPage.eventAutocomplete).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('choose connection', () => {
|
||||
test('choose connection list item', async ({}) => {
|
||||
await flowEditorPage.connectionAutocomplete.click();
|
||||
await flowEditorPage.page.getByRole('listitem').first().click();
|
||||
});
|
||||
|
||||
test('continue to next step', async ({}) => {
|
||||
await flowEditorPage.continueButton.click();
|
||||
});
|
||||
|
||||
test('collapses the substep', async ({}) => {
|
||||
await expect(flowEditorPage.connectionAutocomplete).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('set up action', () => {
|
||||
test('fill topic and message body', async ({}) => {
|
||||
await flowEditorPage.page
|
||||
.getByTestId('parameters.topic-power-input')
|
||||
.locator('[contenteditable]')
|
||||
.fill('Topic');
|
||||
await flowEditorPage.page
|
||||
.getByTestId('parameters.message-power-input')
|
||||
.locator('[contenteditable]')
|
||||
.fill('Message body');
|
||||
});
|
||||
|
||||
test('continue to next step', async ({}) => {
|
||||
await flowEditorPage.continueButton.click();
|
||||
});
|
||||
|
||||
test('collapses the substep', async ({}) => {
|
||||
await expect(flowEditorPage.connectionAutocomplete).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('test trigger', () => {
|
||||
test('show sample output', async ({}) => {
|
||||
await expect(flowEditorPage.testOuput).not.toBeVisible();
|
||||
await flowEditorPage.page
|
||||
.getByTestId('flow-substep-continue-button')
|
||||
.first()
|
||||
.click();
|
||||
await expect(flowEditorPage.testOuput).toBeVisible();
|
||||
await flowEditorPage.screenshot({
|
||||
path: 'Ntfy action test output.png',
|
||||
});
|
||||
await flowEditorPage.continueButton.click();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('publish and unpublish', () => {
|
||||
test('publish flow', async ({}) => {
|
||||
await expect(flowEditorPage.unpublishFlowButton).not.toBeVisible();
|
||||
await expect(flowEditorPage.publishFlowButton).toBeVisible();
|
||||
await flowEditorPage.publishFlowButton.click();
|
||||
await expect(flowEditorPage.publishFlowButton).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('shows read-only sticky snackbar', async ({}) => {
|
||||
await expect(flowEditorPage.infoSnackbar).toBeVisible();
|
||||
await flowEditorPage.screenshot({
|
||||
path: 'Published flow.png',
|
||||
});
|
||||
});
|
||||
|
||||
test('unpublish from snackbar', async ({}) => {
|
||||
await flowEditorPage.page
|
||||
.getByTestId('unpublish-flow-from-snackbar')
|
||||
.click();
|
||||
await expect(flowEditorPage.infoSnackbar).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('publish once again', async ({}) => {
|
||||
await expect(flowEditorPage.publishFlowButton).toBeVisible();
|
||||
await flowEditorPage.publishFlowButton.click();
|
||||
await expect(flowEditorPage.publishFlowButton).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('unpublish from layout top bar', async ({}) => {
|
||||
await expect(flowEditorPage.unpublishFlowButton).toBeVisible();
|
||||
await flowEditorPage.unpublishFlowButton.click();
|
||||
await expect(flowEditorPage.unpublishFlowButton).not.toBeVisible();
|
||||
await flowEditorPage.screenshot({
|
||||
path: 'Unpublished flow.png',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('in layout', () => {
|
||||
test('can go back to flows page', async ({}) => {
|
||||
await flowEditorPage.page.getByTestId('editor-go-back-button').click();
|
||||
await expect(flowEditorPage.page).toHaveURL('/flows');
|
||||
});
|
||||
});
|
@@ -101,7 +101,9 @@ export default function AddNewAppConnection(
|
||||
</InputAdornment>
|
||||
}
|
||||
label={formatMessage('apps.searchApp')}
|
||||
data-test="search-for-app-text-field"
|
||||
inputProps={{
|
||||
'data-test': 'search-for-app-text-field',
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</Box>
|
||||
@@ -109,7 +111,10 @@ export default function AddNewAppConnection(
|
||||
<DialogContent>
|
||||
<List sx={{ pt: 2, width: '100%' }}>
|
||||
{loading && (
|
||||
<CircularProgress sx={{ display: 'block', margin: '20px auto' }} />
|
||||
<CircularProgress
|
||||
data-test="search-for-app-loader"
|
||||
sx={{ display: 'block', margin: '20px auto' }}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!loading &&
|
||||
|
@@ -37,10 +37,12 @@ function ExecutionStepDate(props: Pick<IExecutionStep, 'createdAt'>) {
|
||||
const relativeCreatedAt = createdAt.toRelative();
|
||||
|
||||
return (
|
||||
<Tooltip title={createdAt.toLocaleString(DateTime.DATETIME_FULL_WITH_SECONDS)}>
|
||||
<Tooltip
|
||||
title={createdAt.toLocaleString(DateTime.DATETIME_FULL_WITH_SECONDS)}
|
||||
>
|
||||
<Typography variant="caption" gutterBottom>
|
||||
{formatMessage('executionStep.executedAt', {
|
||||
datetime: relativeCreatedAt
|
||||
datetime: relativeCreatedAt,
|
||||
})}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
@@ -117,7 +119,7 @@ export default function ExecutionStep(
|
||||
<SearchableJSONViewer data={executionStep.dataIn} />
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel value={activeTabIndex} index={1}>
|
||||
<TabPanel value={activeTabIndex} index={1} data-test="data-out-panel">
|
||||
<SearchableJSONViewer data={executionStep.dataOut} />
|
||||
</TabPanel>
|
||||
|
||||
|
@@ -60,11 +60,13 @@ const PowerInput = (props: PowerInputProps) => {
|
||||
const [showVariableSuggestions, setShowVariableSuggestions] =
|
||||
React.useState(false);
|
||||
|
||||
const disappearSuggestionsOnShift = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
const disappearSuggestionsOnShift = (
|
||||
event: React.KeyboardEvent<HTMLInputElement>
|
||||
) => {
|
||||
if (event.code === 'Tab') {
|
||||
setShowVariableSuggestions(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const stepsWithVariables = React.useMemo(() => {
|
||||
return processStepWithExecutions(priorStepsWithExecutions);
|
||||
@@ -112,7 +114,10 @@ const PowerInput = (props: PowerInputProps) => {
|
||||
}}
|
||||
>
|
||||
{/* ref-able single child for ClickAwayListener */}
|
||||
<ChildrenWrapper style={{ width: '100%' }} data-test="power-input">
|
||||
<ChildrenWrapper
|
||||
style={{ width: '100%' }}
|
||||
data-test={`${name}-power-input`}
|
||||
>
|
||||
<FakeInput disabled={disabled}>
|
||||
<InputLabelWrapper>
|
||||
<InputLabel
|
||||
@@ -140,7 +145,10 @@ const PowerInput = (props: PowerInputProps) => {
|
||||
/>
|
||||
</FakeInput>
|
||||
{/* ghost placer for the variables popover */}
|
||||
<div ref={editorRef} style={{ position: 'absolute', right: 16, left: 16 }} />
|
||||
<div
|
||||
ref={editorRef}
|
||||
style={{ position: 'absolute', right: 16, left: 16 }}
|
||||
/>
|
||||
|
||||
<Popper
|
||||
open={showVariableSuggestions}
|
||||
|
@@ -14,6 +14,7 @@ type TextFieldProps = {
|
||||
name: string;
|
||||
clickToCopy?: boolean;
|
||||
readOnly?: boolean;
|
||||
'data-test'?: string;
|
||||
} & MuiTextFieldProps;
|
||||
|
||||
const createCopyAdornment = (
|
||||
@@ -44,6 +45,7 @@ export default function TextField(props: TextFieldProps): React.ReactElement {
|
||||
disabled = false,
|
||||
onBlur,
|
||||
onChange,
|
||||
'data-test': dataTest,
|
||||
...textFieldProps
|
||||
} = props;
|
||||
|
||||
@@ -82,6 +84,9 @@ export default function TextField(props: TextFieldProps): React.ReactElement {
|
||||
readOnly,
|
||||
endAdornment: clickToCopy ? createCopyAdornment(inputRef) : null,
|
||||
}}
|
||||
inputProps={{
|
||||
'data-test': dataTest,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
22
yarn.lock
22
yarn.lock
@@ -3380,6 +3380,16 @@
|
||||
dependencies:
|
||||
"@octokit/openapi-types" "^11.2.0"
|
||||
|
||||
"@playwright/test@^1.36.2":
|
||||
version "1.36.2"
|
||||
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.36.2.tgz#9edd68a02b0929c5d78d9479a654ceb981dfb592"
|
||||
integrity sha512-2rVZeyPRjxfPH6J0oGJqE8YxiM1IBRyM8hyrXYK7eSiAqmbNhxwcLa7dZ7fy9Kj26V7FYia5fh9XJRq4Dqme+g==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
playwright-core "1.36.2"
|
||||
optionalDependencies:
|
||||
fsevents "2.3.2"
|
||||
|
||||
"@pmmmwh/react-refresh-webpack-plugin@^0.5.3":
|
||||
version "0.5.4"
|
||||
resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.4.tgz#df0d0d855fc527db48aac93c218a0bf4ada41f99"
|
||||
@@ -7961,6 +7971,11 @@ dotenv@^10.0.0:
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81"
|
||||
integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==
|
||||
|
||||
dotenv@^16.3.1:
|
||||
version "16.3.1"
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e"
|
||||
integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==
|
||||
|
||||
duplexer3@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
|
||||
@@ -9400,7 +9415,7 @@ fs.realpath@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
|
||||
|
||||
fsevents@^2.3.2, fsevents@~2.3.2:
|
||||
fsevents@2.3.2, fsevents@^2.3.2, fsevents@~2.3.2:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
|
||||
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
|
||||
@@ -13928,6 +13943,11 @@ pkg-up@^3.1.0:
|
||||
dependencies:
|
||||
find-up "^3.0.0"
|
||||
|
||||
playwright-core@1.36.2:
|
||||
version "1.36.2"
|
||||
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.36.2.tgz#32382f2d96764c24c65a86ea336cf79721c2e50e"
|
||||
integrity sha512-sQYZt31dwkqxOrP7xy2ggDfEzUxM1lodjhsQ3NMMv5uGTRDsLxU0e4xf4wwMkF2gplIxf17QMBCodSFgm6bFVQ==
|
||||
|
||||
plur@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/plur/-/plur-4.0.0.tgz#729aedb08f452645fe8c58ef115bf16b0a73ef84"
|
||||
|
Reference in New Issue
Block a user