Skip to content

Commit c19b63d

Browse files
authored
Merge pull request #79 from 2pisoftware/develop
Merge develop
2 parents 2f2c746 + bad6051 commit c19b63d

17 files changed

+1833
-3
lines changed

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Convert to LF line endings on checkout.
2+
*.sh text eol=lf

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,10 @@ venv/
5151
.build/environment/dev/Dockerfile
5252

5353
.build/environment/prod/stage/
54-
.build/environment/prod/Dockerfile
54+
.build/environment/prod/Dockerfile
55+
56+
# Playwright Files
57+
test/playwright/node_modules/
58+
test/playwright/test-results/
59+
test/playwright/src/
60+
test/playwright/*.png

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,5 @@ COPY /.codepipeline/local-dev/configs/supervisord/supervisord.conf /etc/supervis
5959

6060
RUN mkdir /run/php
6161

62-
RUN chmod -R 777 start.sh
63-
CMD ["/bootstrap/start.sh"]
62+
RUN chmod -R 777 /bootstrap/start.sh
63+
CMD ["/bootstrap/start.sh"]

test-cli.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env bash
2+
3+
# Verify that we have arguments
4+
if [ $# -eq 0 ]
5+
then
6+
echo "No arguments supplied"
7+
fi
8+
9+
echo "Running $1 tests (reporter: $2)"
10+
11+
SCRIPT_DIR=$(pwd)
12+
13+
MODULE=$1
14+
REPORTER=${2:-"list"}
15+
BROWSER="${3@Q}"
16+
# Go into Playwright test directory
17+
cd test/playwright
18+
19+
# Run tests
20+
npm run build
21+
22+
if [ -z "$BROWSER" ]
23+
then
24+
npx playwright test --reporter $REPORTER --grep "$MODULE"
25+
else
26+
npx playwright test --project $BROWSER --reporter $REPORTER --grep "$MODULE"
27+
fi
28+
29+
# Go back to script directory
30+
cd $SCRIPT_DIR

test/playwright/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*.png
2+
3+
playwright-report/

test/playwright/README.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Get Cmfive Working
2+
3+
- Bring cmfive up using standard docker compose tools
4+
- Wait for everything to be setup, then right click on the nginx webapp container and `Open In Browser`
5+
- If the webpage gives a MONOLOGGER error, run `bash ./.codespaces/scripts/02_postCreateScript.sh`
6+
- If not, Cmfive is ready for your Playwright tests
7+
8+
# Download nvm
9+
10+
- Grab the latest version of nvm from https://nvm.sh using the `curl` command
11+
- Close the current terminal after nvm is downloaded, and open a new terminal
12+
- `nvm install 18`
13+
- `nvm use 18`
14+
15+
# Use the develop branch as the cmfive-boilerplate repo
16+
17+
- `cd cmfive-boilerplate`
18+
- `git switch develop`
19+
20+
# Use the develop branch as the cmfive-core repo
21+
22+
- `cd ../cmfive-core`
23+
- `git switch develop`
24+
25+
# Ensure styles are compiled
26+
27+
- `cd ./composer/vendor/2pisoftware/cmfive-core/system/templates/base`
28+
- `npm i`
29+
- `npm run dev`
30+
31+
# Setup Playwright
32+
33+
- `cd ../../../../cmfive-boilerplate/test/playwright`
34+
- `npm i`
35+
- `npx playwright install`
36+
- `npx playwright install-deps`
37+
- `npm run setup`
38+
- `npm run build` (run this every time you have changes to a test or utils file that you want to include in the next test run, and for utils files specifically those changes to be present when importing via `@utils/*`)
39+
40+
# Run Playwright Tests
41+
42+
- cwd should be `cmfive-boilerplate/test/playwright/`
43+
- `npm run cleanup` (important! do this every time before you run tests on a system after already having done so previously)
44+
- `npm run test`
45+
- to run tests for a specific platform: `npm run test --platform="[insert browser]"`
46+
- Example: `npm run test --platform="firefox"`
47+
- to run a specific test file: `npm run test --module="[insert test file name]"`
48+
- Example: `npm run test --module="admin"`
49+
- see: https://playwright.dev/docs/test-cli (look for `--grep` instead of `--module`)
50+
- if you want your tests to be attempted a certain number of times (for flaky tests) when they fail: `npm run test:with-retries`
51+
- Example: `npm run test:with-retries --module="task|timelog" --attempts=2 --clean` (`--clean` runs `npm run cleanup` BEFORE your tests run if you have a possibly dirty setup, the script automatically handles the rest of the cleanups for retries)
52+
53+
# Setting up a new Playwright test for a module
54+
55+
- cd into the top level of the module (for example, cwd could be `cmfive-core/system/modules/help`)
56+
- `bash /workspaces/codespace_dev_box/cmfive-boilerplate/test/playwright/mapToPlaywright.sh`
57+
- the above script should be run whenever the relative path between a module and the base playwright directory in `cmfive-boilerplate` changes
58+
- `*.test.ts` files contain the actual test code
59+
- `*.utils.ts` files contain utility functions for a given module
60+
- util files are imported like so: `import { AdminHelper } from '@utils/admin'`, without `.utils.ts` file extension

test/playwright/build.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const glob = require('glob');
2+
const fs = require('fs');
3+
const path = require('path');
4+
5+
function copyFiles(sourceGlob, destDir, rename = fileName => fileName) {
6+
// Find all files and directories that match the glob pattern
7+
glob(sourceGlob, { nodir: true }).then(files => {
8+
// Create the destination directory if it doesn't exist
9+
if (!fs.existsSync(destDir)) {
10+
fs.mkdirSync(destDir);
11+
}
12+
13+
// Copy each file to the destination directory
14+
files.forEach(file => {
15+
const fileName = rename(path.basename(file));
16+
const destFile = path.join(destDir, fileName);
17+
fs.copyFileSync(file, destFile);
18+
});
19+
}).catch(err => {
20+
console.error(err);
21+
})
22+
}
23+
24+
// pull all module test files into src directory
25+
copyFiles('../../system/modules/**/tests/acceptance/playwright/*.test.ts', './src')
26+
copyFiles('../../modules/**/tests/acceptance/playwright/*.test.ts', './src')
27+
28+
// pull all module test util files into one directory for scoping
29+
copyFiles('../../system/modules/**/tests/acceptance/playwright/*.utils.ts', './src/utils',
30+
fileName => fileName.replace('.utils.ts', '.ts')
31+
);
32+
copyFiles('../../modules/**/tests/acceptance/playwright/*.utils.ts', './src/utils',
33+
fileName => fileName.replace('.utils.ts', '.ts')
34+
);
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/bin/bash
2+
3+
BASE_DIR="../../system/modules"
4+
5+
matches=$(find "$BASE_DIR" -path "*/install/migrations/*_TestMigration.php")
6+
7+
if [ -z "$matches" ]; then
8+
echo -e "\n\033[0;33mNo migration test files found.\033[0m"
9+
exit 0
10+
fi
11+
12+
declare -a deletable
13+
declare -a undeletable
14+
15+
for file in $matches; do
16+
if [ -f "$file" ] && [ -w "$(dirname "$file")" ]; then
17+
deletable+=("$file")
18+
else
19+
undeletable+=("$file")
20+
fi
21+
done
22+
23+
if [ ${#undeletable[@]} -ne 0 ]; then
24+
echo -e "\n\033[0;41mThese files cannot be deleted using the current permissions (you might need to use sudo):\033[0m"
25+
echo -e -n "\033[0;31m"
26+
for file in "${undeletable[@]}"; do
27+
echo " $file"
28+
done
29+
echo -e -n "\033[0m"
30+
fi
31+
32+
if [ ${#deletable[@]} -ne 0 ]; then
33+
echo -e "\n\033[0;42mThese files can be deleted using the current permissions:\033[0m"
34+
35+
echo -e -n "\033[0;32m"
36+
for file in "${deletable[@]}"; do
37+
echo " $file"
38+
done
39+
echo -e -n "\033[0m"
40+
41+
echo
42+
echo -e "\033[0;33m"
43+
read -p "Do you want to delete the files you have permission to delete? (y/n) " confirm_delete
44+
echo -e "\033[0m"
45+
46+
if [[ "$confirm_delete" =~ ^[Yy]$ ]]; then
47+
for file in "${deletable[@]}"; do
48+
rm -v "$file"
49+
done
50+
else
51+
echo "No files deleted."
52+
fi
53+
else
54+
echo -e "\nThere are no files to delete with the current permissions."
55+
fi

test/playwright/cmfive.utils.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { Page, expect } from "@playwright/test";
2+
import { DateTime } from "luxon";
3+
4+
export const HOST = (process.env.TEST_HOST ?? "http://127.0.0.1") + ":" + (process.env.TEST_PORT ?? "3000");
5+
export const GLOBAL_TIMEOUT = +(process.env.TIMEOUT ?? 30_000);
6+
7+
export class CmfiveHelper {
8+
static randomID = (prefix: string) => prefix + (Math.random() + 1).toString(36).substring(7)
9+
10+
static async login(page: Page, user: string, password: string)
11+
{
12+
await page.goto(HOST + "/auth/login");
13+
await page.locator("#login").fill(user);
14+
await page.locator("#password").fill(password);
15+
await page.getByRole("button", {name: "Login"}).click();
16+
}
17+
18+
static async logout(page: Page)
19+
{
20+
await page.goto(HOST + "/auth/logout");
21+
}
22+
23+
static async isBootstrap5(page: Page)
24+
{
25+
try {
26+
await page.locator('.body')
27+
} catch (e) {
28+
await page.waitForSelector('.body');
29+
}
30+
return await page.locator('html.theme').count() > 0
31+
}
32+
33+
static getRowByText(page: Page, text: string)
34+
{
35+
return page.locator("tr", { has: page.getByText(text, {exact: true}) }); // page.locator('tr:has-text("' + text + '")');
36+
}
37+
38+
static async clickCmfiveNavbar(page: Page, category: string, option: string)
39+
{
40+
const navbarCategory = page.locator("#topnav_" + category.toLowerCase().split(" ").join("_"));
41+
const bootstrap5 = await this.isBootstrap5(page);
42+
if (bootstrap5) {
43+
await navbarCategory.click();
44+
} else { // Foundation
45+
await navbarCategory.hover();
46+
}
47+
48+
await navbarCategory.getByText(option).click();
49+
}
50+
51+
// Call exactly once per test before any dialogs pop up
52+
static async acceptDialog(page: Page)
53+
{
54+
page.on('dialog', dialog => dialog.accept());
55+
}
56+
57+
static async fillDatePicker(page: Page, datePickerTitle: string, field: string, date: DateTime) {
58+
await page.getByText(datePickerTitle).click();
59+
await page.keyboard.type(date.toFormat("ddLLyyyy"));
60+
61+
const expectedDate = date.toISODate();
62+
if(expectedDate == null)
63+
throw new Error("date.toISODate() returned null");
64+
else
65+
await expect(page.locator("#" + field)).toHaveValue(expectedDate);
66+
}
67+
68+
static async fillAutoComplete(page: Page, field: string, search: string, value: string) {
69+
await page.locator('#acp_' + field).click();
70+
await page.keyboard.type(search);
71+
await page.locator('.ui-menu-item :text("' + value + '")').click();
72+
}
73+
}

test/playwright/mapToPlaywright.sh

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/bin/bash
2+
3+
to_relative_path() {
4+
# The target path is the first argument
5+
local target_path=$1
6+
7+
# The base path is the second argument or the current working directory if not provided
8+
local base_path=${2:-$(pwd)}
9+
10+
# Use realpath command to compute the relative path
11+
echo "$(realpath --relative-to="$base_path" "$target_path")"
12+
}
13+
14+
# Get name of module and create playwright test directory structure
15+
module_name=$(basename $(pwd))
16+
mkdir -p ./tests/acceptance/playwright/
17+
18+
# Get the absolute path of the base playwright directory (the directory containing this script)
19+
playwright_dir_abs=$(cd $(dirname "$0") && pwd -P)
20+
21+
# Get relative path to the base playwright directory from module's playwright test directory
22+
playwright_dir_rel=$(to_relative_path $playwright_dir_abs ./tests/acceptance/playwright/)
23+
24+
# Check if the playwright test file for the module already exists
25+
if [ ! -f ./tests/acceptance/playwright/${module_name}.test.ts ]; then
26+
# Copy boilerplate test file to module's playwright test directory if test file not present
27+
cp $playwright_dir_abs/test_boilerplate.ts ./tests/acceptance/playwright/${module_name}.test.ts
28+
fi
29+
30+
# Check if tsconfig.json exists
31+
if [ -f ./tests/acceptance/playwright/tsconfig.json ]; then
32+
# Update the baseUrl of the pre-existing tsconfig.json
33+
jq --arg playwright_dir_rel "$playwright_dir_rel" '.compilerOptions.baseUrl = $playwright_dir_rel' ./tests/acceptance/playwright/tsconfig.json > ./tests/acceptance/playwright/tsconfig.tmp.json && mv ./tests/acceptance/playwright/tsconfig.tmp.json ./tests/acceptance/playwright/tsconfig.json
34+
else
35+
# Generate new tsconfig.json if it doesn't exist
36+
jq --arg playwright_dir_rel "$playwright_dir_rel" '.compilerOptions.baseUrl = $playwright_dir_rel' $playwright_dir_abs/test_boilerplate.tsconfig.json > ./tests/acceptance/playwright/tsconfig.json
37+
fi

0 commit comments

Comments
 (0)