Skip to content

Commit

Permalink
Move online smoke test to playwright (#4567)
Browse files Browse the repository at this point in the history
  • Loading branch information
sgalsaleh authored Apr 23, 2024
1 parent 8f79d9d commit ac84096
Show file tree
Hide file tree
Showing 6 changed files with 235 additions and 5 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/build-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -635,8 +635,6 @@ jobs:
k8s-distribution: ${{ matrix.cluster.distribution }}
k8s-version: ${{ matrix.cluster.version }}
k8s-instance-type: ${{ matrix.cluster.instance_type }}
testim-access-token: '${{ secrets.TESTIM_ACCESS_TOKEN }}'
testim-branch: ${{ github.head_ref == 'main' && 'master' || github.head_ref }}
aws-access-key-id: '${{ secrets.E2E_SUPPORT_BUNDLE_AWS_ACCESS_KEY_ID }}'
aws-secret-access-key: '${{ secrets.E2E_SUPPORT_BUNDLE_AWS_SECRET_ACCESS_KEY }}'
replicated-api-token: '${{ secrets.C11Y_MATRIX_TOKEN }}'
Expand Down Expand Up @@ -4125,7 +4123,6 @@ jobs:
- ci-test-kots
# testim tests
- validate-existing-online-install-minimal
- validate-smoke-test
- validate-version-history-pagination
- validate-change-license
- validate-min-kots-version
Expand All @@ -4137,6 +4134,7 @@ jobs:
- validate-support-bundle
- validate-gitops
# playwright tests
- validate-smoke-test
- validate-backup-and-restore
- validate-no-required-config
- validate-config
Expand Down
3 changes: 2 additions & 1 deletion e2e/inventory/inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ func NewRegressionTest() Test {

func NewSmokeTest() Test {
return Test{
ID: "smoke-test",
Name: "Smoke Test",
TestimSuite: "smoke-test",
Namespace: "smoke-test",
AppSlug: "qakotstestim",
UpstreamURI: "qakotstestim/github-actions-qa",
NeedsSnapshots: true,
}
Expand Down
3 changes: 2 additions & 1 deletion e2e/playwright/tests/shared/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './login';
export * from './upload-license';
export * from './upload-license';
export * from './validate-deploy-logs';
12 changes: 12 additions & 0 deletions e2e/playwright/tests/shared/validate-deploy-logs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const validateDeployLogs = async (page, expect) => {
await expect(page.getByText('dryrunStdout')).toBeVisible();
await expect(page.getByText('dryrunStderr')).toBeVisible();
await expect(page.getByText('applyStdout')).toBeVisible();
await expect(page.getByText('applyStderr')).toBeVisible();
await expect(page.getByText('helmStdout')).toBeVisible();
await expect(page.getByText('helmStderr')).toBeVisible();
await page.getByText('dryrunStderr').click();
await page.getByText('applyStdout').click();
await expect(page.locator('.view-lines')).toContainText('created');
await page.getByRole('button', { name: 'Ok, got it!' }).click();
};
23 changes: 23 additions & 0 deletions e2e/playwright/tests/smoke-test/license.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
apiVersion: kots.io/v1beta1
kind: License
metadata:
name: githubactionqa
spec:
appSlug: qakotstestim
channelID: 1qcbLyQFdfxq28RbSN4wEtqaxcZ
channelName: Github Actions QA
customerName: GitHub Action QA
endpoint: https://replicated.app
entitlements:
expires_at:
description: License Expiration
title: Expiration
value: ""
valueType: String
isAirgapSupported: true
isGitOpsSupported: true
isSnapshotSupported: true
licenseID: 1qcbXQRwHNCbYIchxIDwuLdLkVa
licenseSequence: 1
licenseType: prod
signature: eyJsaWNlbnNlRGF0YSI6ImV5SmhjR2xXWlhKemFXOXVJam9pYTI5MGN5NXBieTkyTVdKbGRHRXhJaXdpYTJsdVpDSTZJa3hwWTJWdWMyVWlMQ0p0WlhSaFpHRjBZU0k2ZXlKdVlXMWxJam9pWjJsMGFIVmlZV04wYVc5dWNXRWlmU3dpYzNCbFl5STZleUpzYVdObGJuTmxTVVFpT2lJeGNXTmlXRkZTZDBoT1EySlpTV05vZUVsRWQzVk1aRXhyVm1FaUxDSnNhV05sYm5ObFZIbHdaU0k2SW5CeWIyUWlMQ0pqZFhOMGIyMWxjazVoYldVaU9pSkhhWFJJZFdJZ1FXTjBhVzl1SUZGQklpd2lZWEJ3VTJ4MVp5STZJbkZoYTI5MGMzUmxjM1JwYlNJc0ltTm9ZVzV1Wld4SlJDSTZJakZ4WTJKTWVWRkdaR1o0Y1RJNFVtSlRUalIzUlhSeFlYaGpXaUlzSW1Ob1lXNXVaV3hPWVcxbElqb2lSMmwwYUhWaUlFRmpkR2x2Ym5NZ1VVRWlMQ0pzYVdObGJuTmxVMlZ4ZFdWdVkyVWlPakVzSW1WdVpIQnZhVzUwSWpvaWFIUjBjSE02THk5eVpYQnNhV05oZEdWa0xtRndjQ0lzSW1WdWRHbDBiR1Z0Wlc1MGN5STZleUpsZUhCcGNtVnpYMkYwSWpwN0luUnBkR3hsSWpvaVJYaHdhWEpoZEdsdmJpSXNJbVJsYzJOeWFYQjBhVzl1SWpvaVRHbGpaVzV6WlNCRmVIQnBjbUYwYVc5dUlpd2lkbUZzZFdVaU9pSWlMQ0oyWVd4MVpWUjVjR1VpT2lKVGRISnBibWNpZlgwc0ltbHpRV2x5WjJGd1UzVndjRzl5ZEdWa0lqcDBjblZsTENKcGMwZHBkRTl3YzFOMWNIQnZjblJsWkNJNmRISjFaU3dpYVhOVGJtRndjMmh2ZEZOMWNIQnZjblJsWkNJNmRISjFaWDE5IiwiaW5uZXJTaWduYXR1cmUiOiJleUpzYVdObGJuTmxVMmxuYm1GMGRYSmxJam9pV2tKVWMyMWxRVW81YW5Vd01EVmlSbkpGVDNWMlpYRkNkMmt5UjNjeE5rSXhNRXR6VWtSb1JqRm9kMVYwVTNSTWVWRkhUR2g2Ym5OWFVIRndVR1JYTmxsSFZrOVBOWFpDVlU1RGJIZHpURTlETDIxRUt6TnhOWEJNVGxkdVF6SmplbTVGTDA0eE1EVnJWSFJYVDNGa1RXdzFTa3B3T1dwbFNETkhNbm93Y21kelFXOVRZVWd6Y20xMmNYZERhekpGTldabmExZE1Sa0U0TDBGbVNYZEZRbU5FY1dOdFpEWnViVFJFYkZoQlJVcE9hRlJUYjFWNVpTdEpXR2xKTlZGWFZuTmlhR2ROWml0bVYyeG1lV2hRYkVsMmNDODVjVUYyTDI1blFUTkhSVmh0UVZOeU1XSXlhVkZpU25SaVpGQlVkM1JDVFdoalJscDRabE53WlZaRk4xWk9MelJ4VG1GdE1ubGpOMEpEZW1GcGNtdG1PRzlETUZkc1JtaHpWVFkzSzB0ck9HRmxRM0pKVEZCR1ZWcHVjVFF5ZUdaMVZ6UXZWekJXU21VemVrVkRjSGx4YmtkMlptNDNPRlZVYTJoblRuVXlWemRuUFQwaUxDSndkV0pzYVdOTFpYa2lPaUl0TFMwdExVSkZSMGxPSUZCVlFreEpReUJMUlZrdExTMHRMVnh1VFVsSlFrbHFRVTVDWjJ0eGFHdHBSemwzTUVKQlVVVkdRVUZQUTBGUk9FRk5TVWxDUTJkTFEwRlJSVUYyWVRFNVRFUlFWQ3R6VTJaeE5XMU9iemw2YzF4dVNIRk5LMGhSSzFoclEyTk5WRTFaTUZGR1lTdDNRa1p6WWtwdFVFTjVheXRqTTNORmRUTTFSMDVZUTBsamVqWlFUbk0yZGtOdWFuQmhNVFJwZGpOMVdseHVSRmxzY0hCU2QyZzFPSEJ5UldoYVZrOUhXVmxhYm5KeVZIWXllV3Q2VUhWMFNuZFdUazFhUWpSQ1RsTjRWalZFYURCd1dFZ3hWaXRxV1V0VmJFRlJhMXh1Y25aUVZVRk5ObmsxUTBscmVGUjBOM3B1U0U5eVMwWlJRMVJGZWxOS1lsUllUalJPYjNkdVdscFJZVFY2ZWxsWldsTTRkV0pHVFVaNEswVlRVbk5wYjF4dWJWUXZWMEpEUzA1RlIzbEpOMlY0WkdoTlZHMXFNRFUyVkZoUmFHaG1VVk01WkZVclpYTk5NelprSzJsSVRHTkRaeTlyWmtaNVlTdFRiM1puVlVJdmJGeHVZVWhCU0ZKM2RrTkJLMjVzTkRKelpFSlZkRXBIU21VcmRpOUtPVVYxY2tod01pdFNRMnM0Ylc1aFVYUkdNVTh5TmpKeFExUXhaQ3R5Tm5oTWEzcHpORnh1WTFGSlJFRlJRVUpjYmkwdExTMHRSVTVFSUZCVlFreEpReUJMUlZrdExTMHRMVnh1SWl3aWEyVjVVMmxuYm1GMGRYSmxJam9pWlhsS2VtRlhaSFZaV0ZJeFkyMVZhVTlwU25sTk0yUkhWbXR3YzFReFdqQmpNSGcwV1d4R1NsRnFTbEpNTUZKdVRUSkdSVlZVYUhSamJFcE5Za2hvY1dWRlZtMWpXR1JJWkdwYWNWa3dkR0ZPVkdoaFZHcFNWRmRyZUVOVFNFSkpaRlJaY21ReVJuaGliWEF4VGpKS1dXSnFaSFpQV0ZVeVVrZFNhVkV3TVcxWmJrNWFXV3h3UTJKR1ZuZFdSRlpUVjBkdk0xa3lXakpWVms1RFkwaE9ZVk5zU2pCa1dIQlJZMWh3UjFwc1FtMWliVmt3VkRGSmNtTkhPVk5sYld4RlRsaHdTV0ZwT1Vsa1ZYUk9XVEprZVZsVVRrZGxWWE15WW5wU2JGTnNSbWxaYTFKWFpHMXNTVkpUZEhSYWEwa3hZMjFXYmxSR1RqRldSVTUxVjJ0amVGWnVTa1ZPYTJocVZEQmFhMlZFVGpOU1IxcEdZbFJvZWxOV1dsZE9XRmt5VlVoVk5WRnJhRVJPZW1nd1VqRk9SRTFFU1ROUlZtUmFWR3RrZWxkRlZqTmtNMmN5VVZkMFZGTXdXbXROTVhCRFRVWnZkMkY1ZEd4Wk0yczFUMWRPVUUxc1NuUlVTRkpGVFcwNVEwNHdSak5PYlhBeVRVVktOVmxWUmxST2JYUnlTekZrU0Zvd09XaFhhbGwzVjBOMFdGSnVXbWxOUkVweFZGZFpNMk50ZEV4VlZYaDZXbXhDTlZaVVpGQmhSWGN6VVcxS1JWZEdSazVrUkVwYVdXMTRNMUpZY0UxVmF6bHFWMnhhTm1GWVdrOWxiRnBPVXpKbmNsWnRZemxRVTBselNXMWtjMkl5U21oaVJYUnNaVlZzYTBscWIybFpiVkpzV2xSVk1rNVVXWGRaTWxwcFRrUk9hazlYU1hsUFIwcHRUMVJvYkZsWFRtaGFiVVV5VGtSWmFXWlJQVDBpZlE9PSJ9
195 changes: 195 additions & 0 deletions e2e/playwright/tests/smoke-test/test.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import { test, expect } from '@playwright/test';
import { login, uploadLicense, validateDeployLogs } from '../shared';

const { execSync } = require("child_process");

test('smoke test', async ({ page }) => {
test.setTimeout(5 * 60 * 1000); // 5 minutes
await login(page);
await uploadLicense(page, expect);
await expect(page.locator('#app')).toContainText('Install in airgapped environment', { timeout: 15000 });
await page.getByText('download App Name from the Internet').click();
await expect(page.locator('#app')).toContainText('Installing your license');
await expect(page.locator('h3')).toContainText('My Example Config', { timeout: 30000 });
await page.locator('#a_bool-group').getByText('a bool field').click();
await page.locator('#a_required_text-group').getByRole('textbox').click();
await page.locator('#a_required_text-group').getByRole('textbox').fill('my required text field');
await expect(page.locator('#version_sequence-group')).toContainText('This version is 0');
await page.getByRole('button', { name: 'Continue' }).click();
await expect(page.locator('#app')).toContainText('Results', { timeout: 30000 });
await expect(page.locator('#app')).toContainText('Sequence is 0');
await page.getByRole('button', { name: 'Deploy' }).click();
await page.getByRole('button', { name: 'Deploy anyway' }).click();
await expect(page.locator('#app')).toContainText('Ready', { timeout: 30000 });
await expect(page.locator('#app')).toContainText('Currently deployed version', { timeout: 15000 });
await expect(page.locator('#app')).toContainText('Check for update');
await expect(page.locator('#app')).toContainText('Configure automatic updates');
await expect(page.locator('#app')).toContainText('Redeploy', { timeout: 15000 });
await page.getByRole('link', { name: 'Version history' }).click();
await expect(page.locator('.currentVersion--wrapper')).toContainText('Sequence 0');
await expect(page.locator('#app')).toContainText('Currently deployed version');
await expect(page.locator('#app')).toContainText('Check for update');
await expect(page.locator('#app')).toContainText('Configure automatic updates');
await expect(page.getByRole('button')).toContainText('Redeploy');
await page.getByText('Configure automatic updates').click();
await expect(page.locator('.ConfigureUpdatesModal')).toContainText('Default');
await expect(page.locator('.ConfigureUpdatesModal')).toContainText('Every 4 hours');
await expect(page.locator('label')).toContainText('Enable automatic deployment');
await page.locator('.replicated-select__control').click();
await page.locator('.replicated-select__option').getByText('Weekly', { exact: true }).click();
await expect(page.locator('.ConfigureUpdatesModal')).toContainText('Weekly');
await expect(page.locator('.ConfigureUpdatesModal')).toContainText('At 12:00 AM, only on Sunday');
await page.getByRole('button', { name: 'Update', exact: true }).click();
await expect(page.getByText('Automatically check for updates', { exact: true })).not.toBeVisible();
await page.locator('span[data-tip="View deploy logs"]').first().click();
await validateDeployLogs(page, expect);
await page.reload();
await page.getByRole('link', { name: 'Dashboard' }).click();
await expect(page.getByText('App Name')).toBeVisible();
await expect(page.locator('.Dashboard--appIcon')).toBeVisible();
await expect(page.locator('p').filter({ hasText: 'License' })).toBeVisible();
await page.getByText('Configure automatic updates').click();
await expect(page.locator('.ConfigureUpdatesModal')).toContainText('Weekly');
await expect(page.locator('.ConfigureUpdatesModal')).toContainText('At 12:00 AM, only on Sunday');
await expect(page.locator('label')).toContainText('Enable automatic deployment');
await page.getByRole('button', { name: 'Cancel' }).click();
await page.locator('svg.icons.clickable[data-tip="View release notes"]').click();
await expect(page.getByLabel('Release Notes').getByRole('paragraph')).toContainText('release notes - updates');
await page.getByRole('button', { name: 'Close' }).click();
await page.locator('span[data-tip="View deploy logs"]').click();
await validateDeployLogs(page, expect);
await page.getByRole('link', { name: 'Config', exact: true }).click();
await expect(page.locator('h3')).toContainText('My Example Config');
await expect(page.locator('#version_sequence-group')).toContainText('This version is 1');
await expect(page.getByRole('button', { name: 'Save config' })).toBeVisible();
await page.getByRole('link', { name: 'Troubleshoot' }).click();
await expect(page.getByRole('button', { name: 'Analyze App Name' })).toBeVisible();
await page.getByRole('link', { name: 'License' }).click();
await expect(page.locator('#app')).toContainText('Airgap enabled');
await expect(page.locator('#app')).toContainText('Snapshots enabled');
await expect(page.getByRole('button', { name: 'Sync license' })).toBeVisible();
await page.getByRole('link', { name: 'View files' }).click();
await page.getByText('upstream', { exact: true }).click();
await page.getByRole('listitem', { name: 'config.yaml' }).locator('div').click();
await expect(page.locator('.view-lines')).toContainText('apiVersion');
await page.getByText('Click here', { exact: true }).click();
await expect(page.getByRole('heading')).toContainText('Edit patches for your kots application');
await expect(page.getByText('Copy command').first()).toBeVisible();

let downloadCommand = await page.locator('.react-prism.language-bash').first().textContent();
if (!downloadCommand!.includes('download')) {
throw new Error("Expected the download command to contain the word 'download'");
}
downloadCommand = `${downloadCommand} --overwrite`;
console.log(downloadCommand, "\n");
execSync(downloadCommand, {stdio: 'inherit'});

await expect(page.getByText('Copy command').last()).toBeVisible();
let uploadCommand = await page.locator('.react-prism.language-bash').last().textContent();
if (!uploadCommand!.includes('upload')) {
throw new Error("Expected the upload command to contain the word 'upload'");
}
console.log(uploadCommand, "\n");
execSync(uploadCommand, {stdio: 'inherit'});

await page.getByRole('button', { name: 'Ok, got it!' }).click();
await page.getByRole('link', { name: 'Version history' }).click();
await expect(page.locator('#app')).toContainText('KOTS Upload');
await expect(page.getByText('Running checks', { exact: true }).first()).not.toBeVisible({ timeout: 30000 });
await page.getByRole('button', { name: 'Deploy' }).first().click();
await page.getByRole('button', { name: 'Deploy this version' }).click();
await expect(page.locator('#app')).toContainText('Deploying');
await expect(page.locator('#app')).toContainText('Currently deployed version');
await expect(page.locator('#app')).toContainText('Application up to date.');
await expect(page.locator('.currentVersion--wrapper')).toContainText('Sequence 1');
await page.getByRole('link', { name: 'Registry settings' }).click();
await page.getByPlaceholder('artifactory.some-big-bank.com').click();
await page.getByPlaceholder('artifactory.some-big-bank.com').fill('ttl.sh');
await page.getByPlaceholder('username').click();
await page.getByPlaceholder('username').fill('admin');
await page.getByPlaceholder('password').click();
await page.getByPlaceholder('password').fill('admin');
await page.getByRole('button', { name: 'Test connection' }).click();
await expect(page.locator('form')).toContainText('Success!');
await page.getByRole('button', { name: 'Save changes' }).click();
await expect(page.getByRole('button', { name: 'Save changes' })).toBeDisabled();
await expect(page.locator('.Loader')).toBeVisible();
await expect(page.locator('#app')).toContainText('Writing manifest to image destination', { timeout: 30000 });
await expect(page.getByRole('button', { name: 'Save changes' })).toBeEnabled({ timeout: 30000 });
await expect(page.locator('.Loader')).not.toBeVisible();
await page.getByRole('link', { name: 'Version history' }).click();
await expect(page.locator('#app')).toContainText('Registry Change');
await expect(page.locator('#app')).toContainText('Sequence 2');
await page.getByRole('link', { name: 'Registry settings' }).click();
await page.getByRole('button', { name: 'Stop using registry' }).click();
await page.getByRole('button', { name: 'OK' }).click();
await expect(page.locator('.Loader')).toBeVisible();
await expect(page.getByRole('button', { name: 'Stop using registry' })).toBeDisabled();
await expect(page.getByRole('button', { name: 'Save changes' })).toBeEnabled({ timeout: 30000 });
await expect(page.locator('.Loader')).not.toBeVisible();
await expect(page.getByPlaceholder('artifactory.some-big-bank.com')).toBeEmpty();
await expect(page.getByPlaceholder('username')).toBeEmpty();
await expect(page.getByPlaceholder('password')).toBeEmpty();
await expect(page.getByPlaceholder('namespace')).toBeEmpty();
await page.waitForTimeout(2000);
await page.getByRole('link', { name: 'Version history' }).click();
await expect(page.locator('#app')).toContainText('Sequence 3', { timeout: 10000 });
await expect(page.locator('#app')).toContainText('Registry Change');
await expect(page.locator('.NavItem').getByText('Application', { exact: true })).toBeVisible();
await expect(page.locator('.NavItem').getByText('GitOps', { exact: true })).toBeVisible();
await expect(page.locator('.NavItem').getByText('Snapshots', { exact: true })).toBeVisible();
await expect(page.locator('div').filter({ hasText: /^Change passwordAdd new applicationLog out$/ }).getByRole('img')).toBeVisible();
await page.locator('.NavItem').getByText('Snapshots', { exact: true }).click();
await page.getByRole('link', { name: 'Settings & Schedule' }).click();
await expect(page.locator('#app')).toContainText('Snapshot settings');
await page.getByText('+ Add a new destination').click();
await expect(page.getByRole('button', { name: 'Check for Velero' })).toBeVisible();
await page.getByRole('button', { name: 'Check for Velero' }).click();
await expect(page.getByLabel('Modal')).toContainText('Velero is installed on your cluster');
await page.getByRole('button', { name: 'Ok, got it!' }).click();
await page.getByRole('link', { name: 'Full Snapshots (Instance)' }).click();
await expect(page.locator('#app')).toContainText('No snapshots yet');
await page.getByRole('button', { name: 'Start a snapshot' }).click();
await expect(page.locator('#app')).toContainText('In Progress');
await expect(page.locator('#app')).toContainText('Completed', { timeout: 300000 });
await page.getByText('Learn more').click();
await page.getByRole('button', { name: 'Ok, got it!' }).click();
await expect(page.locator('#app')).toContainText('Full Snapshots (Instance)');
await page.getByRole('link', { name: 'Partial Snapshots (Application)' }).click();
await page.getByRole('button', { name: 'Start a snapshot' }).click();
await expect(page.locator('#app')).toContainText('In Progress');
await expect(page.locator('#app')).toContainText('Completed', { timeout: 30000 });
await expect(page.getByText('It’s recommend that you use')).toBeVisible();
await page.getByText('Learn more').click();
await page.getByRole('button', { name: 'Ok, got it!' }).click();
await expect(page.locator('#app')).toContainText('Partial snapshots (Application)');
await page.getByRole('link', { name: 'Full Snapshots (Instance)', exact: true }).click();
await page.locator('.SnapshotRow--wrapper').click();
await expect(page.locator('#app')).toContainText('Snapshot timeline');
await page.getByText('View logs').click();
await expect(page.locator('.view-lines')).toContainText('level=info');
await page.getByRole('button', { name: 'Ok, got it!' }).click();
await page.getByRole('link', { name: 'Full Snapshots (Instance)', exact: true }).click();
await page.locator('svg.icons.clickable[data-tip="Restore from this backup"]').click();
await expect(page.getByLabel('Modal')).toContainText('Restore from backup');
await expect(page.getByLabel('Modal')).toContainText('Admin console & application');
await expect(page.getByLabel('Modal')).toContainText('Application & metadata only');
await expect(page.getByLabel('Modal')).toContainText('Only restores the admin console');
await page.getByText('Application & metadata only', { exact: true }).click();
await page.getByRole('button', { name: 'Cancel' }).click();
await page.locator('svg.icons.clickable').last().click();
await expect(page.getByLabel('Modal')).toContainText('Delete snapshot');
await page.getByRole('button', { name: 'Delete snapshot' }).click();
await expect(page.locator('#app')).toContainText('Deleting');
await expect(page.locator('#app')).toContainText('No snapshots yet', { timeout: 15000 });
await page.getByRole('link', { name: 'Settings & Schedule' }).click();
await page.getByRole('button', { name: 'Update storage settings' }).click();
await expect(page.locator('form')).toContainText('Settings updated', { timeout: 30000 });
await page.getByRole('button', { name: 'Update schedule' }).click();
await expect(page.locator('#app')).toContainText('Schedule updated');
await page.locator('div').filter({ hasText: /^Change passwordAdd new applicationLog out$/ }).getByRole('img').click();
await page.getByText('Log out', { exact: true }).click();
await expect(page.getByPlaceholder('password')).toBeVisible({ timeout: 30000 });
await expect(page.locator('#app')).toContainText('Enter the password to access the App Name admin console.');
await expect(page.getByRole('button')).toContainText('Log in');
});

0 comments on commit ac84096

Please sign in to comment.