Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added UI Tests #142

Draft
wants to merge 1 commit into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions .github/workflows/ui-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
name: UI Tests
on:
## Check each PR
push:
pull_request:
## Manual execution on branch
workflow_dispatch:
## Nightly
### Needs secrets
#### GC_PROJECT_ID
#### GC_SERVICE_KEY
#### NIGHTLY_TOKEN
schedule:
- cron: '0 0 * * *'
env:
CAMPAIGN: 'ps_emailalerts'
jobs:
ui_test:
name: UI Tests
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- PS_VERSION: '1.7.7.8'
PHP_VERSION: '7.3'
- PS_VERSION: '1.7.8.11'
PHP_VERSION: '7.4'
- PS_VERSION: '8.0.5'
PHP_VERSION: '8.1'
- PS_VERSION: '8.1.6'
PHP_VERSION: '8.1'
- PS_VERSION: 'nightly'
PHP_VERSION: '8.2'
env:
PS_VERSION: ${{ matrix.PS_VERSION }}
PHP_VERSION: ${{ matrix.PHP_VERSION }}
steps:
- name: Checkout
uses: actions/checkout@v3.1.0

- name: Rights
run: chmod -R 777 ./mails

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.PHP_VERSION }}

- name: Install dependencies
run: composer install --no-dev && composer dump-autoload -o --no-dev

- name: Start containers
working-directory: tests/UI/
run: |
docker compose -f "docker-compose.yml" up -d --build
bash -c 'while [[ "$(curl -L -s -o /dev/null -w %{http_code} http://localhost/en/)" != "200" ]]; do sleep 5; done'

- name: Install dependencies
working-directory: tests/UI/
run: npm ci

- name: Install Playwright Browsers
working-directory: tests/UI/
run: npx playwright install chromium --with-deps

- name: Run Playwright tests
working-directory: tests/UI/
run: npx playwright test

- name: Export Docker errors
working-directory: tests/UI/
if: always()
run: docker compose logs --no-color >& docker-compose.log

- name: Upload artifact
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report-${{ matrix.PS_VERSION }}
path: |
tests/UI/reports/
tests/UI/report.json
tests/UI/docker-compose.log
retention-days: 30

nightly:
name: Nightly Report
if: ${{ github.event_name == 'schedule' }}
needs:
- ui_test
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
PS_VERSION:
- '1.7.7.8'
- '1.7.8.11'
- '8.0.5'
- '8.1.6'
- 'nightly'
permissions:
contents: 'read'
id-token: 'write'
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Download report
uses: actions/download-artifact@v4
with:
name: playwright-report-${{ matrix.PS_VERSION }}
path: tests/UI/

# Nightly : Rename file
- name: "Nightly : Rename file"
working-directory: tests/UI/
run: |
mkdir -p nightly
REPORT_NAME="${{ env.CAMPAIGN }}_$(date +%Y-%m-%d)-${{ matrix.PS_VERSION }}"
mv report.json nightly/${REPORT_NAME}.json

# Nightly : Auth GCP
- name: "Nightly : Auth GCP"
uses: google-github-actions/auth@v1
with:
credentials_json: ${{ secrets.GC_SERVICE_KEY }}
project_id: ${{ secrets.GC_PROJECT_ID }}

# Nightly : Setup GCP
- name: "Nightly : Setup GCP"
uses: google-github-actions/setup-gcloud@v1

# Nightly : Upload to Google Cloud Storage (GCS)
- name: "Nightly : Upload to Google Cloud Storage (GCS)"
working-directory: tests/UI/
run: gsutil cp -r "nightly/**" gs://prestashop-core-nightly/reports

# Nightly : Push Report
- name: "Nightly : Push Report"
run: |
REPORT_NAME="${{ env.CAMPAIGN }}_$(date +%Y-%m-%d)-${{ matrix.PS_VERSION }}"
curl -v "https://api-nightly.prestashop-project.org/import/report/playwright?token=${{ secrets.NIGHTLY_TOKEN }}&filename=${REPORT_NAME}.json&campaign=${{ env.CAMPAIGN }}&platform=chromium"
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
/vendor
.php_cs.cache

## UI Tests
/tests/UI/.env
/tests/UI/node_modules/
/tests/UI/report.json
/tests/UI/reports/
/tests/UI/test-results/
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
import {
test,
expect,
type Page,
type BrowserContext,
} from '@playwright/test';

import {
boDashboardPage,
boLoginPage,
boModuleManagerPage,
boProductsPage,
dataModules,
FakerProduct,
foClassicCategoryPage,
foClassicHomePage,
foClassicProductPage,
opsBOCatalogProduct,
utilsFile,
utilsTest,
} from '@prestashop-core/ui-testing';

const baseContext: string = 'modules_ps_emailalerts_installation_uninstallAndInstallModule';

test.describe('Mail alerts module - Uninstall and install module', async () => {
let browserContext: BrowserContext;
let page: Page;
let idProduct: number;
let nthProduct: number|null;

const productOutOfStockNotAllowed: FakerProduct = new FakerProduct({
name: 'Product Out of stock not allowed',
type: 'standard',
taxRule: 'No tax',
tax: 0,
quantity: 0,
behaviourOutOfStock: 'Deny orders',
});

test.beforeAll(async ({ browser }) => {
browserContext = await browser.newContext();
page = await browserContext.newPage();
});
test.afterAll(async () => {
await page.close();
});

test('PRE-Condition : Create product out of stock not allowed', async () => {
await opsBOCatalogProduct.createProduct(page, productOutOfStockNotAllowed, `${baseContext}_preTest`);
});

// BackOffice - Fetch the ID of the product
test('should login in BO', async () => {
await utilsTest.addContextItem(test.info(), 'testIdentifier', 'loginBO', baseContext);

await boLoginPage.goTo(page, global.BO.URL);
await boLoginPage.successLogin(page, global.BO.EMAIL, global.BO.PASSWD);

const pageTitle = await boDashboardPage.getPageTitle(page);
expect(pageTitle).toContain(boDashboardPage.pageTitle);
});

test('should go to \'Catalog > Products\' page', async () => {
await utilsTest.addContextItem(test.info(), 'testIdentifier', 'goToProductsPage', baseContext);

await boDashboardPage.goToSubMenu(
page,
boDashboardPage.catalogParentLink,
boDashboardPage.productsLink,
);

const pageTitle = await boProductsPage.getPageTitle(page);
expect(pageTitle).toContain(boProductsPage.pageTitle);
});

test('should filter list by \'product_name\'', async () => {
await utilsTest.addContextItem(test.info(), 'testIdentifier', 'filterProductName', baseContext);

await boProductsPage.resetFilter(page);
await boProductsPage.filterProducts(page, 'product_name', productOutOfStockNotAllowed.name, 'input');

const numberOfProductsAfterFilter = await boProductsPage.getNumberOfProductsFromList(page);
expect(numberOfProductsAfterFilter).toEqual(1);

idProduct = await boProductsPage.getTextColumn(page, 'id_product', 1) as number;
});

// BackOffice - Uninstall Module
test('should go to \'Modules > Module Manager\' page', async () => {
await utilsTest.addContextItem(test.info(), 'testIdentifier', 'goToModuleManagerPage', baseContext);

await boDashboardPage.goToSubMenu(
page,
boDashboardPage.modulesParentLink,
boDashboardPage.moduleManagerLink,
);
await boModuleManagerPage.closeSfToolBar(page);

const pageTitle = await boModuleManagerPage.getPageTitle(page);
expect(pageTitle).toContain(boModuleManagerPage.pageTitle);
});

test(`should search the module ${dataModules.psEmailAlerts.name}`, async () => {
await utilsTest.addContextItem(test.info(), 'testIdentifier', 'searchModule', baseContext);

const isModuleVisible = await boModuleManagerPage.searchModule(page, dataModules.psEmailAlerts);
expect(isModuleVisible).toBeTruthy();
});

test('should display the uninstall modal and cancel it', async () => {
await utilsTest.addContextItem(test.info(), 'testIdentifier', 'uninstallModuleAndCancel', baseContext);

const textResult = await boModuleManagerPage.setActionInModule(page, dataModules.psEmailAlerts, 'uninstall', true);
expect(textResult).toEqual('');

const isModuleVisible = await boModuleManagerPage.isModuleVisible(page, dataModules.psEmailAlerts);
expect(isModuleVisible).toBeTruthy();

const isModalVisible = await boModuleManagerPage.isModalActionVisible(page, dataModules.psEmailAlerts, 'uninstall');
expect(isModalVisible).toBeFalsy();

const dirExists = await utilsFile.doesFileExist(`${utilsFile.getRootPath()}/modules/${dataModules.psEmailAlerts.tag}/`);
expect(dirExists).toBeTruthy();
});

test('should uninstall the module', async () => {
await utilsTest.addContextItem(test.info(), 'testIdentifier', 'uninstallModule', baseContext);

const successMessage = await boModuleManagerPage.setActionInModule(page, dataModules.psEmailAlerts, 'uninstall', false);
expect(successMessage).toEqual(boModuleManagerPage.uninstallModuleSuccessMessage(dataModules.psEmailAlerts.tag));

// Check the directory `modules/Modules.psEmailAlerts.tag`
const dirExists = await utilsFile.doesFileExist(`${utilsFile.getRootPath()}/modules/${dataModules.psEmailAlerts.tag}/`);
expect(dirExists).toBeTruthy();
});

// FrontOffice - Check that the module is not present
test('should go to Front Office', async () => {
await utilsTest.addContextItem(test.info(), 'testIdentifier', 'goToFoAfterUninstall', baseContext);

page = await boModuleManagerPage.viewMyShop(page);
await foClassicHomePage.changeLanguage(page, 'en');

const isHomePage = await foClassicHomePage.isHomePage(page);
expect(isHomePage).toBeTruthy();
});

test('should go to the All Products Page', async () => {
await utilsTest.addContextItem(test.info(), 'testIdentifier', 'goToAllProductsPageAfterUninstall', baseContext);

await foClassicHomePage.goToAllProductsPage(page);

const isCategoryPageVisible = await foClassicCategoryPage.isCategoryPage(page);
expect(isCategoryPageVisible, 'Home category page was not opened').toBeTruthy();
});

test('should go the the second page', async () => {
await utilsTest.addContextItem(test.info(), 'testIdentifier', 'goToSecondPage', baseContext);

await foClassicCategoryPage.goToNextPage(page);

nthProduct = await foClassicCategoryPage.getNThChildFromIDProduct(page, idProduct);
expect(nthProduct).not.toBeNull();
});

test('should go to the product page', async () => {
await utilsTest.addContextItem(test.info(), 'testIdentifier', 'goToProductPage', baseContext);

await foClassicCategoryPage.goToProductPage(page, nthProduct!);

const pageTitle = await foClassicProductPage.getPageTitle(page);
expect(pageTitle.toUpperCase()).toContain(productOutOfStockNotAllowed.name.toUpperCase());

const hasFlagOutOfStock = await foClassicProductPage.hasProductFlag(page, 'out_of_stock');
expect(hasFlagOutOfStock).toBeTruthy();

const hasBlockMailAlert = await foClassicProductPage.hasBlockMailAlert(page);
expect(hasBlockMailAlert).toBeFalsy();
});

// BackOffice - Install the module
test('should go back to BO', async () => {
await utilsTest.addContextItem(test.info(), 'testIdentifier', 'goBackToBo', baseContext);

page = await foClassicProductPage.closePage(browserContext, page, 0);
await boModuleManagerPage.reloadPage(page);

const pageTitle = await boModuleManagerPage.getPageTitle(page);
expect(pageTitle).toContain(boModuleManagerPage.pageTitle);
});

test('should install the module', async () => {
await utilsTest.addContextItem(test.info(), 'testIdentifier', 'installModule', baseContext);

const successMessage = await boModuleManagerPage.setActionInModule(page, dataModules.psEmailAlerts, 'install', false);
expect(successMessage).toEqual(boModuleManagerPage.installModuleSuccessMessage(dataModules.psEmailAlerts.tag));

// Check the directory `modules/Modules.psEmailAlerts.tag`
const dirExists = await utilsFile.doesFileExist(`${utilsFile.getRootPath()}/modules/${dataModules.psEmailAlerts.tag}/`);
expect(dirExists).toBeTruthy();
});

// FrontOffice - Check that the module is present
test('should return to Front Office', async () => {
await utilsTest.addContextItem(test.info(), 'testIdentifier', 'goToFo', baseContext);

page = await boModuleManagerPage.viewMyShop(page);
await foClassicHomePage.changeLanguage(page, 'en');

const isHomePage = await foClassicHomePage.isHomePage(page);
expect(isHomePage).toBeTruthy();
});

test('should return to the All Products Page', async () => {
await utilsTest.addContextItem(test.info(), 'testIdentifier', 'goToAllProductsPage', baseContext);

await foClassicHomePage.goToAllProductsPage(page);

const isCategoryPageVisible = await foClassicCategoryPage.isCategoryPage(page);
expect(isCategoryPageVisible, 'Home category page was not opened').toBeTruthy();
});

test('should return to the product page', async () => {
await utilsTest.addContextItem(test.info(), 'testIdentifier', 'goToProductPageWithMailAlert', baseContext);

await foClassicCategoryPage.goToNextPage(page);
await foClassicCategoryPage.goToProductPage(page, nthProduct!);

const pageTitle = await foClassicProductPage.getPageTitle(page);
expect(pageTitle.toUpperCase()).toContain(productOutOfStockNotAllowed.name.toUpperCase());

const hasFlagOutOfStock = await foClassicProductPage.hasProductFlag(page, 'out_of_stock');
expect(hasFlagOutOfStock).toBeTruthy()

const hasBlockMailAlert = await foClassicProductPage.hasBlockMailAlert(page);
expect(hasBlockMailAlert).toBeTruthy();
});

test('POST-Condition : Delete product out of stock not allowed', async () => {
await opsBOCatalogProduct.deleteProduct(page, productOutOfStockNotAllowed, `${baseContext}_postTest_0`);
});
});
Loading
Loading