diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index a74b82020..71f95fc8d 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -1,39 +1,81 @@ name: E2E Tests + on: - push: - branches: - - main + workflow_dispatch: + pull_request: + +# Cancels all previous workflow runs for pull requests that have not completed. +concurrency: + # The concurrency group contains the workflow name and the branch name for pull requests + # or the commit hash for any other events. + group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} + cancel-in-progress: true + jobs: - playwright: - name: Playwright Tests + e2e-tests: runs-on: ubuntu-latest + strategy: + fail-fast: false + # matrix: + # node: ['14'] + steps: - - name: Checkout repository - uses: actions/checkout@v2 - - - name: Log debug information - run: | - npm --version - node --version - git --version - php --version - composer --version - - - name: Install NodeJS - uses: actions/setup-node@v2 - with: - node-version-file: '.nvmrc' - - - name: NPM install - run: npm ci --legacy-peer-deps - - - name: Playwright install - run: npx playwright install --with-deps - - - name: Run Playwright - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - WP_ADMIN_PASSWORD: ${{ secrets.WP_ADMIN_PASSWORD }} - run: npm run test:e2e - if: ${{ success() || failure() }} + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/cache@v4 + id: playwright-cache + with: + path: | + ~/.cache/ms-playwright + key: ${{ runner.os }}-playwright-${{ hashFiles('**/package-lock.json') }} + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + # Enable built-in functionality for caching and restoring dependencies, which is disabled by default. + # The actions/setup-node uses actions/cache under the hood. + # https://github.com/actions/setup-node#caching-global-packages-data + cache: 'npm' + + + # Restoring the short lived node_modules cache + # to be used accross all workflows running on the last commit. + # https://github.com/actions/cache/blob/main/caching-strategies.md#creating-a-short-lived-cache + - uses: actions/cache/restore@v4 + id: node_modules-cache + with: + path: | + ./node_modules + key: ${{ runner.os }}-node_modules-${{ github.sha }}-${{ hashFiles('package-lock.json') }} + + - name: NPM install + if: steps.node_modules-cache.outputs.cache-hit != 'true' + run: npm ci --legacy-peer-deps + + # Creating a short lived node_modules cache + - uses: actions/cache/save@v4 + if: steps.node_modules-cache.outputs.cache-hit != 'true' + with: + path: | + ./node_modules + key: ${{ steps.node_modules-cache.outputs.cache-primary-key }} + + - name: Install Playwright dependencies + run: npx playwright install --with-deps + if: steps.playwright-cache.outputs.cache-hit != 'true' + - run: npx playwright install-deps + if: steps.playwright-cache.outputs.cache-hit == 'true' + + - name: Starting Playwright & Running the tests + run: | + npm run test:e2e + + - name: Retain failed test results + uses: actions/upload-artifact@v3 + if: failure() + with: + name: test-results-matrix-less-debugging + path: artifacts/test-results/ diff --git a/docs/contributor/e2e-tests/README.md b/docs/contributor/e2e-tests/README.md new file mode 100644 index 000000000..484ac646a --- /dev/null +++ b/docs/contributor/e2e-tests/README.md @@ -0,0 +1,30 @@ +# e2e testing + +GatherPress allows to run automated and manual end-to-end tests, while sharing the same, `wp-env` based, setup. + +## Automated tests + +Check the results of the _e2e-tests action workflow_ at `https://github.com/GatherPress/gatherpress/actions/workflows/e2e-tests.yml`. + +## Manual testing + +0. Have `node` installed +1. Have `docker` running +2. Open the plugin folder in a terminal +3. _Choose one of the following options_ + - Run Playwright normally + ``` + npm run test:e2e + ``` + + - Run Playwright visually (and change what's happening) + ``` + npm run test:e2e:ui + ``` + ![grafik](https://github.com/user-attachments/assets/1627dff7-363e-447e-9981-adac610ac888) + + - Run Playwright in debugging mode (and record what's happening) + ``` + npm run test:e2e:debug + ``` + - Run Tests independently _AND_ visually using the [Playwright VSCode extension](https://playwright.dev/docs/getting-started-vscode) \ No newline at end of file diff --git a/package.json b/package.json index fdd5d8ef0..9bd4a9554 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,12 @@ "screenshots:wporg": "wp-scripts test-playwright --config .github/scripts/wordpress-org-screenshots/playwright.config.ts", "screenshots:wporg:debug": "wp-scripts test-playwright --config .github/scripts/wordpress-org-screenshots/playwright.config.ts --debug", "screenshots:wporg:ui": "wp-scripts test-playwright --config .github/scripts/wordpress-org-screenshots/playwright.config.ts --ui", - "test:e2e": "playwright test --config=playwright.config.js", + "pretest:e2e": "wp-scripts build && wp-env start", + "pretest:e2e:debug": "wp-scripts build && wp-env start", + "pretest:e2e:ui": "wp-scripts build && wp-env start", + "test:e2e": "wp-scripts test-playwright --config test/e2e/playwright.config.ts", + "test:e2e:debug": "wp-scripts test-playwright --config test/e2e/playwright.config.ts --debug", + "test:e2e:ui": "wp-scripts test-playwright --config test/e2e/playwright.config.ts --ui", "test:unit:js": "wp-scripts test-unit-js --coverage --testResultsProcessor=jest-sonar-reporter", "pretest:unit:php": "wp-env start --xdebug", "test:unit:php": "wp-env run tests-wordpress php -dxdebug.mode=coverage /var/www/html/wp-content/plugins/gatherpress/vendor/bin/phpunit -c /var/www/html/wp-content/plugins/gatherpress/phpunit.xml.dist --coverage-clover=coverage.xml --coverage-html=build/coverage-report", diff --git a/playwright.config.js b/playwright.config.js deleted file mode 100644 index 64f78ef36..000000000 --- a/playwright.config.js +++ /dev/null @@ -1,83 +0,0 @@ -// @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: './test/e2e', - /* 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: 2, - /* Opt out of parallel tests on CI. */ - workers: process.env.CI ? 1 : undefined, - - timeout: 30000, - /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: '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: 'https://develop.gatherpress.org/', - - /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: 'on-first-retry', - video: 'on', - screenshot: 'on', - }, - - /* 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, - // }, -}); diff --git a/test/e2e/admin-tests/gatherpress-admin-venue.spec.js b/test/e2e/admin-tests/gatherpress-admin-venue.spec.js deleted file mode 100644 index e2ff5d6c7..000000000 --- a/test/e2e/admin-tests/gatherpress-admin-venue.spec.js +++ /dev/null @@ -1,41 +0,0 @@ -const { test } = require('@playwright/test'); -const { login } = require('../reusable-user-steps/common.js'); - -test.describe('e2e test for venue through admin side', () => { - test.beforeEach(async ({ page }) => { - test.setTimeout(120000); - await page.setViewportSize({ width: 1920, height: 720 }); - await page.waitForLoadState('networkidle'); - }); - - test('The admin should be able to create a new post for Venue', async ({ - page, - }) => { - await login({ page, username: 'testuser1' }); - - await page.getByRole('link', { name: 'Events', exact: true }).click(); - - await page.getByRole('link', { name: 'Venues' }).click(); - await page.screenshot({ path: 'vanue-page.png' }); - - await page - .locator('#wpbody-content') - .getByRole('link', { name: 'Add New' }) - .click(); - - await page.getByLabel('Add title').isVisible(); - await page.getByLabel('Add title').fill('Test venue'); - await page.getByLabel('Add title').press('Tab'); - - const venue = await page.$('.gatherpress-venue__name'); - await venue.press('Backspace'); - - await page - .getByLabel('Empty block; start writing or') - .fill('test venue information'); - - await page.getByLabel('Toggle block inserter').click(); - await page.getByRole('option', { name: 'Paragraph' }).click(); - await page.screenshot({ path: 'new-venue.png' }); - }); -}); diff --git a/test/e2e/admin-tests/gatherpress-admin.spec.js b/test/e2e/admin-tests/gatherpress-admin.spec.js deleted file mode 100644 index 91d8a7856..000000000 --- a/test/e2e/admin-tests/gatherpress-admin.spec.js +++ /dev/null @@ -1,29 +0,0 @@ -const { test } = require('@playwright/test'); -const { login } = require('../reusable-user-steps/common.js'); - -test.describe('As admin login into gatherPress', () => { - test.beforeEach(async ({ page }) => { - test.setTimeout(120000); - await page.setViewportSize({ width: 1920, height: 720 }); - await page.waitForLoadState('networkidle'); - }); - - test('The Event menu item should be preloaded after clicking Add New button', async ({ - page, - }) => { - await login({ page, username: 'testuser1' }); - - await page.getByRole('link', { name: 'Events', exact: true }).click(); - await page.screenshot({ path: 'event-page.png' }); - - await page - .locator('#wpbody-content') - .getByRole('link', { name: 'Add New' }) - .click(); - - await page.getByLabel('Document Overview').click(); - - await page.getByLabel('List View').locator('div').nth(1).isVisible(); - await page.screenshot({ path: 'add-new-event.png' }); - }); -}); diff --git a/test/e2e/playwright.config.ts b/test/e2e/playwright.config.ts new file mode 100644 index 000000000..db9d7b5a7 --- /dev/null +++ b/test/e2e/playwright.config.ts @@ -0,0 +1,66 @@ +/** + * External dependencies + */ +import { defineConfig, devices } from '@playwright/test'; + +/** + * WordPress dependencies + * + * Playwright default configuration, that is used & provided by Gutenberg. + * https://github.com/WordPress/gutenberg/blob/trunk/packages/scripts/config/playwright.config.js + */ +// using Object Rest Spread operator magic +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax +// const { use, ...baseConfig} = require( '@wordpress/scripts/config/playwright.config' ); +const { ...baseConfig} = require( '@wordpress/scripts/config/playwright.config' ); + +export default defineConfig({ + ...baseConfig, + + // This directory holds all the test files. + // https://playwright.dev/docs/api/class-testconfig#test-config-test-dir + // + // IDEA: Maybe this should be set to "../../src/" + // where the test files would be housed directly with their components, blocks, etc. + testDir: 'tests', + + // Configure projects for major browsers + // We can test on different or multiple browsers if needed. + // https://playwright.dev/docs/test-projects#configure-projects-for-multiple-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' }, + // }, + ], +}); \ No newline at end of file diff --git a/test/e2e/tests/gatherpress-admin-settings.spec.ts b/test/e2e/tests/gatherpress-admin-settings.spec.ts new file mode 100644 index 000000000..f7ae4f74a --- /dev/null +++ b/test/e2e/tests/gatherpress-admin-settings.spec.ts @@ -0,0 +1,27 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +test.describe( 'GatherPress Settings', () => { + test('A link to the plugin settings page is present under the Events menu', async ({ + page, + admin, + }) => { + await admin.visitAdminPage('/'); + + const gatherPressMenuItem = page.locator('li', { + has: page.getByRole('link', { name: 'Events' }), + }); + const wpGatherPressSettingsItem = gatherPressMenuItem.getByRole('link', { + name: 'Settings', + }); + const wpGatherPressSettingsItemUrl = + await wpGatherPressSettingsItem.getAttribute('href'); + + await expect(wpGatherPressSettingsItem).toBeVisible(); + await expect(wpGatherPressSettingsItemUrl).toContain( + 'edit.php?post_type=gatherpress_event&page=gatherpress_general', + ); + }); +}); \ No newline at end of file diff --git a/test/e2e/tests/gatherpress-admin-venue.spec.ts b/test/e2e/tests/gatherpress-admin-venue.spec.ts new file mode 100644 index 000000000..a01f8e412 --- /dev/null +++ b/test/e2e/tests/gatherpress-admin-venue.spec.ts @@ -0,0 +1,32 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + + +// const { login } = require('../reusable-user-steps/common.js'); + +test.describe('e2e test for venue through admin side', () => { + + test('The admin should be able to create a new post for Venue', async ({ + admin, + page, + }) => { + await admin.createNewPost( { postType: 'gatherpress_venue' } ); + + await page.getByLabel('Add title').isVisible(); + await page.getByLabel('Add title').fill('Test venue'); + await page.getByLabel('Add title').press('Tab'); + + const venue = await page.$('.gatherpress-venue__name'); + await venue.press('Backspace'); + + await page + .getByLabel('Empty block; start writing or') + .fill('test venue information'); + + await page.getByLabel('Toggle block inserter').click(); + await page.getByRole('option', { name: 'Paragraph' }).click(); + // await page.screenshot({ path: 'new-venue.png' }); + }); +}); diff --git a/test/e2e/tests/gatherpress-admin.spec.ts b/test/e2e/tests/gatherpress-admin.spec.ts new file mode 100644 index 000000000..880c49c22 --- /dev/null +++ b/test/e2e/tests/gatherpress-admin.spec.ts @@ -0,0 +1,34 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + + +test.describe('As admin login into gatherPress', () => { + + test('The Event menu item should be preloaded after clicking Add New button', async ({ + page, + }) => { + await page.getByRole('link', { name: 'Events', exact: true }).click(); + // await page.screenshot({ path: 'event-page.png' }); + +/* await page + .locator('#wpbody-content') + .getByRole('link', { name: 'Add New' }) + .click(); + + await page.getByLabel('Document Overview').click(); + + await page.getByLabel('List View').locator('div').nth(1).isVisible(); + await page.screenshot({ path: 'add-new-event.png' }); */ + + // Maybe better use ... ? + // await admin.createNewPost( { postType: 'gatherpress_event' } ); + + +/* await page.getByRole('link', { name: 'Events', exact: true }).click(); + + await page.getByRole('link', { name: 'Venues' }).click(); + await page.screenshot({ path: 'vanue-page.png' }); */ + }); +}); diff --git a/test/e2e/tests/gatherpress-blocks-editor.spec.ts b/test/e2e/tests/gatherpress-blocks-editor.spec.ts new file mode 100644 index 000000000..5a2431c2a --- /dev/null +++ b/test/e2e/tests/gatherpress-blocks-editor.spec.ts @@ -0,0 +1,76 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +test.describe( 'GatherPress general block tests', () => { + + test.beforeEach( async ( { admin } ) => { + await admin.createNewPost(); + } ); + + test.afterEach( async ( { requestUtils } ) => { + await requestUtils.deleteAllPosts(); + } ); + + /** + * Are all blocks available? + * + * Tests if all blocks are avail through the block inserter panel. + * + * Adopted from 'Search for the Paragraph block with 2 additional variations' + * @source https://github.com/WordPress/gutenberg/blob/ddadd1a95d18270908ac4a1fd8d6e354cfadf61c/test/e2e/specs/editor/plugins/block-variations.spec.js#L62 + */ + test( 'Is 1 block available?', async ( { + page, + } ) => { + await page + .getByRole( 'button', { name: 'Toggle block inserter' } ) + .click(); + + await page + .getByRole( 'region', { name: 'Block Library' } ) + .getByRole( 'searchbox', { + name: 'Search for blocks and patterns', + } ) + .fill( 'gatherpress' ); + + await expect( + page + .getByRole( 'listbox', { name: 'Blocks' } ) + .getByRole( 'option' ) + ).toHaveText( [ 'Events List' ] ); + } ); + + /* + * Not working yet, because the GatherPress blocks are still all on apiVersion 2, + * but need to have 3 + * / + test( 'Does the "Events List" block insert?', async ( { + page, + editor, + } ) => { + + await editor.insertBlock( { name: 'Events List' } ); + + + const block = editor.canvas.getByRole( 'document', { + name: 'Events List', + } ); + + await expect( block ).not.toBeNull(); */ + +/* + await editor.canvas + .getByLabel('Add default block') + .click(); + + await page.keyboard.type( '/Events List' ); + await page.keyboard.press( 'Enter' ); + + await expect( + editor.canvas.getByRole( 'document', { name: 'Events List' } ) + ).toHaveText( '...' ); * / + } ); */ + +} ); \ No newline at end of file diff --git a/test/e2e/admin-tests/gatherpress-event-loggedIn.spec.js b/test/e2e/tests/gatherpress-event-loggedIn.spec.ts similarity index 81% rename from test/e2e/admin-tests/gatherpress-event-loggedIn.spec.js rename to test/e2e/tests/gatherpress-event-loggedIn.spec.ts index 869a76174..b80969307 100644 --- a/test/e2e/admin-tests/gatherpress-event-loggedIn.spec.js +++ b/test/e2e/tests/gatherpress-event-loggedIn.spec.ts @@ -1,22 +1,15 @@ -const { test } = require('@playwright/test'); -const { login } = require('../reusable-user-steps/common'); +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); test.describe('e2e test for publish event through admin side', () => { - test.beforeEach(async ({ page }) => { - test.setTimeout(120000); - await page.setViewportSize({ width: 1920, height: 720 }); - await page.waitForLoadState('networkidle'); - await login({ page, username: 'testuser1' }); - }); test('01-the user should be able to publish an online event', async ({ + admin, page, }) => { - await page.getByRole('link', { name: 'Events', exact: true }).click(); - await page - .locator('#wpbody-content') - .getByRole('link', { name: 'Add New' }) - .click(); + await admin.createNewPost( { postType: 'gatherpress_event' } ); await page .getByLabel('Block: Event Date') @@ -27,7 +20,7 @@ test.describe('e2e test for publish event through admin side', () => { await page.getByRole('button', { name: 'Event settings' }).click(); - await page.getByLabel('Venue Selector').selectOption('58:online-event'); + await page.getByLabel('Venue Selector').selectOption('58:online-event'); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!! 58 doesn't exist const currentDate = new Date().toISOString().split('T')[0]; // format YYYY-MM-DD const eventTitle = await page .getByLabel('Add title') @@ -55,7 +48,7 @@ test.describe('e2e test for publish event through admin side', () => { .filter({ hasText: 'View Event' }) .isVisible({ timeout: 30000 }); // verified the view event button. }); - +/* test('02-verify the logged in user view RSVP button on home page and perform RSVP action', async ({ page, }) => { @@ -84,5 +77,5 @@ test.describe('e2e test for publish event through admin side', () => { .locator('.gatherpress-rsvp-response__items') .first() .screenshot({ path: 'attending.png' }); - }); + }); */ }); diff --git a/test/e2e/admin-tests/gatherpress-event-nonLoggegIn.spec.js b/test/e2e/tests/gatherpress-event-nonLoggegIn.spec.js.DISABLED similarity index 100% rename from test/e2e/admin-tests/gatherpress-event-nonLoggegIn.spec.js rename to test/e2e/tests/gatherpress-event-nonLoggegIn.spec.js.DISABLED diff --git a/test/e2e/admin-tests/gatherpress-event.spec.js b/test/e2e/tests/gatherpress-event.spec.js.DISABLED similarity index 100% rename from test/e2e/admin-tests/gatherpress-event.spec.js rename to test/e2e/tests/gatherpress-event.spec.js.DISABLED