Просмотр исходного кода

Merge pull request #8885 from weseek/support/148123-replace-tests-with-playwright

support: Replace tests with playwright (21-basic-features-for-guest/21-basic-features-for-guest--access-to-page)
Shun Miyazawa 1 год назад
Родитель
Сommit
f35d941ae7

+ 14 - 6
.github/workflows/reusable-app-prod.yml

@@ -425,12 +425,6 @@ jobs:
       run: |
         cat config/ci/.env.local.for-auto-install >> .env.production.local
 
-    # - name: Copy dotenv file for automatic installation with allowing guest mode
-    #   if: ${{ matrix.spec-group == '21' }}
-    #   working-directory: ./apps/app
-    #   run: |
-    #     cat config/ci/.env.local.for-auto-install-with-allowing-guest >> .env.production.local
-
     - name: Playwright Run
       working-directory: ./apps/app
       run: |
@@ -440,6 +434,20 @@ jobs:
         MONGO_URI: mongodb://mongodb:27017/growi-playwright
         ELASTICSEARCH_URI: http://localhost:${{ job.services.elasticsearch.ports['9200'] }}/growi
 
+    - name: Copy dotenv file for automatic installation with allowing guest mode
+      working-directory: ./apps/app
+      run: |
+        cat config/ci/.env.local.for-auto-install-with-allowing-guest >> .env.production.local
+
+    - name: Playwright Run (--project=${browser}/guest-mode)
+      working-directory: ./apps/app
+      run: |
+        yarn playwright test --project=${{ matrix.browser }}/guest-mode --shard=${{ matrix.shard }}
+      env:
+        HOME: /root # ref: https://github.com/microsoft/playwright/issues/6500
+        MONGO_URI: mongodb://mongodb:27017/growi-playwright-guest-mode
+        ELASTICSEARCH_URI: http://localhost:${{ job.services.elasticsearch.ports['9200'] }}/growi
+
     - name: Slack Notification
       uses: weseek/ghaction-slack-notification@master
       if: failure()

+ 22 - 24
apps/app/playwright.config.ts

@@ -1,10 +1,28 @@
 import fs from 'node:fs';
 import path from 'node:path';
 
-import { defineConfig, devices } from '@playwright/test';
+import { defineConfig, devices, type Project } from '@playwright/test';
 
 const authFile = path.resolve(__dirname, './playwright/.auth/admin.json');
 
+// Use prepared auth state.
+const storageState = fs.existsSync(authFile) ? authFile : undefined;
+
+const supportedBrowsers = ['chromium', 'firefox', 'webkit'] as const;
+
+const projects: Array<Project> = supportedBrowsers.map(browser => ({
+  name: browser,
+  use: { ...devices[`Desktop ${browser}`], storageState },
+  testIgnore: /(10-installer|21-basic-features-for-guest)\/.*\.spec\.ts/,
+  dependencies: ['setup', 'auth'],
+}));
+
+const projectsForGuestMode: Array<Project> = supportedBrowsers.map(browser => ({
+  name: `${browser}/guest-mode`,
+  use: { ...devices[`Desktop ${browser}`] }, // Do not use storageState
+  testMatch: /21-basic-features-for-guest\/.*\.spec\.ts/,
+}));
+
 /**
  * Read environment variables from file.
  * https://github.com/motdotla/dotenv
@@ -49,9 +67,6 @@ export default defineConfig({
     trace: 'on-first-retry',
 
     viewport: { width: 1400, height: 1024 },
-
-    // Use prepared auth state.
-    storageState: fs.existsSync(authFile) ? authFile : undefined,
   },
 
   /* Configure projects for major browsers */
@@ -62,31 +77,14 @@ export default defineConfig({
 
     {
       name: 'chromium/installer',
-      use: { ...devices['Desktop Chrome'] },
+      use: { ...devices['Desktop Chrome'], storageState },
       testMatch: /10-installer\/.*\.spec\.ts/,
       dependencies: ['setup'],
     },
 
-    {
-      name: 'chromium',
-      use: { ...devices['Desktop Chrome'] },
-      testIgnore: /10-installer\/.*\.spec\.ts/,
-      dependencies: ['setup', 'auth'],
-    },
-
-    {
-      name: 'firefox',
-      use: { ...devices['Desktop Firefox'] },
-      testIgnore: /10-installer\/.*\.spec\.ts/,
-      dependencies: ['setup', 'auth'],
-    },
+    ...projects,
 
-    {
-      name: 'webkit',
-      use: { ...devices['Desktop Safari'] },
-      testIgnore: /10-installer\/.*\.spec\.ts/,
-      dependencies: ['setup', 'auth'],
-    },
+    ...projectsForGuestMode,
 
     /* Test against mobile viewports. */
     // {

+ 45 - 0
apps/app/playwright/21-basic-features-for-guest/access-to-page.spec.ts

@@ -0,0 +1,45 @@
+import { test, expect } from '@playwright/test';
+
+import { collapseSidebar } from '../utils';
+
+test('/Sandbox is successfully loaded', async({ page }) => {
+
+  await page.goto('/Sandbox');
+
+  // Expect a title "to contain" a substring.
+  await expect(page).toHaveTitle(/Sandbox/);
+});
+
+test('/Sandbox/math is successfully loaded', async({ page }) => {
+
+  await page.goto('/Sandbox/Math');
+
+  // Check if the math elements are visible
+  await expect(page.locator('.math').first()).toBeVisible();
+});
+
+test('Access to /me page', async({ page }) => {
+  await page.goto('/me');
+
+  // Expect to be redirected to /login when accessing /me
+  await expect(page.getByTestId('login-form')).toBeVisible();
+});
+
+test('Access to /trash page', async({ page }) => {
+  await page.goto('/trash');
+
+  // Expect the trash page specific elements to be present when accessing /trash
+  await expect(page.getByTestId('trash-page-list')).toBeVisible();
+});
+
+// TODO: Improve collapseSidebar (https://redmine.weseek.co.jp/issues/148538)
+// test('Access to /tags page', async({ page }) => {
+//   await page.goto('/tags');
+
+//   await collapseSidebar(page, false);
+//   await page.getByTestId('grw-sidebar-nav-primary-tags').click();
+//   await expect(page.getByTestId('grw-sidebar-content-tags')).toBeVisible();
+//   await expect(page.getByTestId('grw-tags-list').first()).toBeVisible();
+//   await expect(page.getByTestId('grw-tags-list').first()).toContainText('You have no tag, You can set tags on pages');
+//   await expect(page.getByTestId('tags-page')).toBeVisible();
+// });

+ 21 - 0
apps/app/playwright/utils/CollapseSidebar.ts

@@ -0,0 +1,21 @@
+// TODO: https://redmine.weseek.co.jp/issues/148538
+import { expect, type Page } from '@playwright/test';
+
+export const collapseSidebar = async(page: Page, isCollapsed: boolean): Promise<void> => {
+  const isSidebarContentsHidden = !(await page.getByTestId('grw-sidebar-contents').isVisible());
+  if (isSidebarContentsHidden === isCollapsed) {
+    return;
+  }
+
+  const collapseSidebarToggle = page.getByTestId('btn-toggle-collapse');
+  await expect(collapseSidebarToggle).toBeVisible();
+
+  await collapseSidebarToggle.click();
+
+  if (isCollapsed) {
+    await expect(page.locator('.grw-sidebar-dock')).not.toBeVisible();
+  }
+  else {
+    await expect(page.locator('.grw-sidebar-dock')).toBeVisible();
+  }
+};

+ 1 - 0
apps/app/playwright/utils/index.ts

@@ -0,0 +1 @@
+export * from './CollapseSidebar';

+ 0 - 73
apps/app/test/cypress/e2e/21-basic-features-for-guest/21-basic-features-for-guest--access-to-page.cy.ts

@@ -1,78 +1,5 @@
-context('Access to page by guest', () => {
-  const ssPrefix = 'access-to-page-by-guest-';
-
-  it('/Sandbox is successfully loaded', () => {
-    cy.visit('/Sandbox');
-    cy.waitUntilSkeletonDisappear();
-
-    cy.collapseSidebar(true, true);
-    cy.screenshot(`${ssPrefix}-sandbox`);
-  });
-
-  // TODO: https://redmine.weseek.co.jp/issues/109939
-  it('/Sandbox with anchor hash is successfully loaded', () => {
-    cy.visit('/Sandbox#headers');
-    cy.collapseSidebar(true);
-
-    // assert the element is in viewport
-    cy.get('#headers').should('be.inViewport');
-
-    // remove animation for screenshot
-    // remove 'blink' class because ::after element cannot be operated
-    // https://stackoverflow.com/questions/5041494/selecting-and-manipulating-css-pseudo-elements-such-as-before-and-after-usin/21709814#21709814
-    cy.get('#headers').invoke('removeClass', 'blink');
-
-    // cy.waitUntilSkeletonDisappear();
-    cy.screenshot(`${ssPrefix}-sandbox-headers`);
-  });
-
-  it('/Sandbox/Math is successfully loaded', () => {
-    cy.visit('/Sandbox/Math');
-    cy.collapseSidebar(true);
-
-    // for check download toc data
-    // https://redmine.weseek.co.jp/issues/111384
-    // cy.get('.toc-link').should('be.visible');
-
-    cy.get('.math').should('be.visible');
-
-    // cy.waitUntilSkeletonDisappear();
-    cy.screenshot(`${ssPrefix}-sandbox-math`);
-  });
-
-  it('/Sandbox with edit is successfully loaded', () => {
-    cy.visit('/Sandbox#edit');
-    cy.collapseSidebar(true);
-
-    // cy.waitUntilSkeletonDisappear();
-    cy.screenshot(`${ssPrefix}-sandbox-with-edit-hash`);
-  })
-
-});
-
-
-context('Access to /me page', () => {
-  const ssPrefix = 'access-to-me-page-by-guest-';
-
-  it('/me should be redirected to /login', () => {
-    cy.visit('/me');
-    cy.getByTestid('login-form').should('be.visible');
-    cy.screenshot(`${ssPrefix}-me`);
-  });
-
-});
-
-
 context('Access to special pages by guest', () => {
   const ssPrefix = 'access-to-special-pages-by-guest-';
-
-  it('/trash is successfully loaded', () => {
-    cy.visit('/trash', {  });
-    cy.getByTestid('trash-page-list').should('be.visible');
-    cy.collapseSidebar(true);
-    cy.screenshot(`${ssPrefix}-trash`);
-  });
-
   it('/tags is successfully loaded', () => {
     cy.visit('/tags');