Compare commits
	
		
			5 Commits
		
	
	
		
			js-in-ts
			...
			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