Skip to content
Merged
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
97 changes: 47 additions & 50 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ pipeline {
agent any

environment {
PLAYWRIGHT_IMAGE = 'mcr.microsoft.com/playwright:v1.52.0-noble'
PLAYWRIGHT_IMAGE = 'mcr.microsoft.com/playwright:v1.56.0-noble'
WORK_DIR = '/app'
REPORT_DIR = 'reports/playwright'
REPORT_DIR = 'reports/'
BASE_URL = 'https://your-jenkins-instance.com' // Replace with your Jenkins base URL
}

stages {
Expand Down Expand Up @@ -40,51 +41,47 @@ pipeline {

stage('Run Tests') {
steps {
def injectCredsIfExists = {
def creds = [:]
creds.URL = ''
creds.USERNAME = ''
creds.PASSWORD = ''
catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
script {
def testUrl = ''
def testUser = ''
def testPass = ''

try {
withCredentials([
string(credentialsId: 'TEST_URL', variable: 'TEST_URL'),
usernamePassword(credentialsId: 'TEST_CREDENTIALS', usernameVariable: 'TEST_USERNAME', passwordVariable: 'TEST_PASSWORD')
]) {
creds.URL = env.'TEST_URL'
creds.USERNAME = env.'TEST_USERNAME'
creds.PASSWORD = env.'TEST_PASSWORD'
echo "Injected credentials for ${prefix}"
}
} catch (ignored) {
echo "No credentials found for ${prefix}, using default values"
string(credentialsId: 'TEST_URL', variable: 'TEST_URL'),
usernamePassword(credentialsId: 'TEST_CREDENTIALS', usernameVariable: 'TEST_USERNAME', passwordVariable: 'TEST_PASSWORD')
]) {
testUrl = env.TEST_URL
testUser = env.TEST_USERNAME
testPass = env.TEST_PASSWORD
echo 'Credentials injected'
}
} catch (ignored) {
echo 'No credentials found, using defaults'
}
return creds
}

catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
script {
def creds = injectCredsIfExists()
try {
sh """
docker run --rm \
-v "${env.WORKSPACE}:${WORK_DIR}" \
-w ${WORK_DIR} \
-e JENKINS=true \
-e ENVIRONMENT="${params.ENVIRONMENT}" \
-e PLAYWRIGHT_HTML_REPORT_DIR="${REPORT_DIR}/playwright-report" \
-e CUSTOM_REPORT_DIR="${REPORT_DIR}" \
-e ${prefix}_URL="${creds.URL}" \
-e ${prefix}_USERNAME="${creds.USERNAME}" \
-e ${prefix}_PASSWORD="${creds.PASSWORD}" \
${PLAYWRIGHT_IMAGE} bash -c '
mkdir -p "${REPORT_DIR}"
npx playwright test --project=chromium
'
"""
} finally {
archiveArtifacts artifacts: 'test-results/**/*', allowEmptyArchive: true
}
def jobPath = env.JOB_NAME.tokenize('/').collect { "job/${it.replaceAll(' ', '%20')}" }.join('/')
def reportUrl = "${BASE_URL}/${jobPath}/${env.BUILD_NUMBER}"

sh """
docker run --rm \
-v "${env.WORKSPACE}:${WORK_DIR}" \
-w ${WORK_DIR} \
-e JENKINS=true \
-e ENVIRONMENT="${params.ENVIRONMENT}" \
-e PLAYWRIGHT_HTML_REPORT_DIR="${REPORT_DIR}/playwright-report" \
-e CUSTOM_REPORT_DIR="${REPORT_DIR}" \
-e JENKINS_URL="${reportUrl}" \
-e JENKINS_TEST_RESULTS="${reportUrl}/artifact" \
-e URL="${testUrl}" \
-e USERNAME="${testUser}" \
-e PASSWORD="${testPass}" \
${PLAYWRIGHT_IMAGE} bash -c '
mkdir -p "${REPORT_DIR}"
npx playwright test --project=chromium
'
"""
}
}
}
Expand All @@ -93,14 +90,14 @@ pipeline {
stage('Publish HTML Report') {
steps {
publishHTML(target: [
reportName: 'Playwright Report',
reportDir: "${env.REPORT_DIR}",
reportFiles: 'detailed-report.html',
keepAll: true,
alwaysLinkToLastBuild: true,
allowMissing: false,
escapeHtml: false
])
reportName: 'Test Results Dashboard',
reportDir: "${REPORT_DIR}",
reportFiles: 'detailed-report.html',
keepAll: true,
alwaysLinkToLastBuild: true,
allowMissing: false,
escapeHtml: false
])
}
}
}
Expand Down
24 changes: 12 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "��#\u0000 \u0000p\u0000l\u0000a\u0000y\u0000w\u0000r\u0000i\u0000g\u0000h\u0000t\u0000-\u0000t\u0000y\u0000p\u0000e\u0000s\u0000c\u0000r\u0000i\u0000p\u0000t\u0000-\u0000t\u0000e\u0000m\u0000p\u0000l\u0000a\u0000t\u0000e\u0000\r\u0000 \u0000",
"main": "index.js",
"scripts": {
"setup": "npx playwright install && npm ci",
"setup": "npm ci && npx playwright install",
"prettier": "npx prettier --check .",
"prettier:fix": "npx prettier --write .",
"eslint": "npx eslint .",
Expand All @@ -19,6 +19,7 @@
"test:custom": "ts-node utils/run-custom-tests.ts",
"test:report:playwright": "playwright show-report reports/playwright-report",
"test:report:custom": "open-cli reports/index.html",
"test:report:detailed": "open-cli reports/detailed-report.html",
"prepare": "husky && husky install"
},
"lint-staged": {
Expand All @@ -39,9 +40,9 @@
},
"homepage": "https://github.com/IQGeo/playwright-typescript-template#readme",
"devDependencies": {
"@playwright/test": "^1.53.1",
"@types/node": "^24.0.3",
"@eslint/js": "^9.24.0",
"@playwright/test": "^1.56.0",
"@types/node": "^24.0.3",
"@typescript-eslint/eslint-plugin": "^8.29.1",
"@typescript-eslint/parser": "^8.29.1",
"eslint": "^9.24.0",
Expand Down
24 changes: 19 additions & 5 deletions src/pages/api/user-page.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { APIRequestContext } from "@playwright/test";
import { Logger } from "shared/utils";

export class UserApi {
private readonly url;
Expand All @@ -7,12 +8,25 @@
}

async getUser(id: number) {
const response = await this.request.get(`${this.url}/users/${id}`);
return response;
try {
const response = await this.request.get(`${this.url}/users/${id}`);
Logger.info(`Received response for user with ID: ${id}`);
return response;
} catch (error) {
const errorMessage = error instanceof Error ? error : new Error(String(error));
Logger.error(`Error fetching user with ID: ${id} - ${errorMessage.message}`);
throw errorMessage;
}
}

async createUser(data: any) {

Check warning on line 21 in src/pages/api/user-page.ts

View workflow job for this annotation

GitHub Actions / code-quality-check

Unexpected any. Specify a different type
const response = await this.request.post(`${this.url}/users`, { data });
return response;
try {
const response = await this.request.post(`${this.url}/users`, { data });
Logger.info("Created user successfully");
return response;
} catch (error) {
const errorMessage = error instanceof Error ? error : new Error(String(error));
Logger.error(`Error creating user - ${errorMessage.message}`);
throw errorMessage;
}
}
}
3 changes: 2 additions & 1 deletion src/shared/mock-data/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./todo-mock-data";
export * from "./todo-data";
export * from "./user-data";
6 changes: 6 additions & 0 deletions src/shared/mock-data/user-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { User } from "shared/types";

export const NEW_USER: User = {
name: "John Doe",
email: "john@example.com",
};
26 changes: 18 additions & 8 deletions src/shared/utils/custom-logger.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
import test from "@playwright/test";
import fs from "fs";
import path from "path";
import { LogLevel } from "shared/types";

export class Logger {
private static logFilePath = path.resolve("logs", "automation.log");
private static logFilePath = path.join(__dirname, "logs", "automation.log");

public static write(level: LogLevel, message: string) {
private static log(level: LogLevel, message: string) {
const timestamp = new Date().toLocaleString();
const logMessage = `${timestamp} ${level}: ${message}`;
console.log(logMessage);

Check warning on line 11 in src/shared/utils/custom-logger.ts

View workflow job for this annotation

GitHub Actions / code-quality-check

Unexpected console statement
fs.mkdirSync(path.dirname(Logger.logFilePath), { recursive: true });
fs.appendFileSync(Logger.logFilePath, logMessage + "\n");
}
}

// Logger.write("INFO", "Validate ADMS event shows up in the details panel");
public static info(message: any) {

Check warning on line 16 in src/shared/utils/custom-logger.ts

View workflow job for this annotation

GitHub Actions / code-quality-check

Unexpected any. Specify a different type
Logger.log("INFO", message);
}

public static warn(message: any) {

Check warning on line 20 in src/shared/utils/custom-logger.ts

View workflow job for this annotation

GitHub Actions / code-quality-check

Unexpected any. Specify a different type
Logger.log("WARN", message);
}

public static error(message: any) {

Check warning on line 24 in src/shared/utils/custom-logger.ts

View workflow job for this annotation

GitHub Actions / code-quality-check

Unexpected any. Specify a different type
Logger.log("ERROR", message);
}

// await test.step("Validate ADMS event shows up in the details panel", async () => {
// await expect(page.locator("#feature-header").getByText("ADMS Event", { exact: true })).toBeVisible();
// });
public static debug(message: any) {

Check warning on line 28 in src/shared/utils/custom-logger.ts

View workflow job for this annotation

GitHub Actions / code-quality-check

Unexpected any. Specify a different type
Logger.log("DEBUG", message);
}
}
43 changes: 29 additions & 14 deletions src/tests/api/user.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { test, expect } from "@playwright/test";
import { TAGS } from "@utils/constants";
import { TAGS } from "@utils/configuration";
import { UserApi } from "pages/api";
import { User } from "shared/types";
import { NEW_USER } from "shared/mock-data/user-data";

test.describe("User API Tests", () => {
let userApi: UserApi;
Expand All @@ -16,10 +16,27 @@ test.describe("User API Tests", () => {
tag: TAGS.REGRESSION,
},
async () => {
const response = await userApi.getUser(1);
const user = await response.json();
expect(response.ok()).toBeTruthy();
expect(user.name).toBeDefined();
await test.step("Request users api to get user by id and validate results", async () => {
const response = await userApi.getUser(1);
const user = await response.json();
expect(response.ok()).toBeTruthy();
expect(user.name).toBeDefined();
});
},
);

test(
"should not fetch user by ID - 0 (negative case)",
{
tag: TAGS.REGRESSION,
},
async () => {
await test.step("Request users api to get user by id and validate results", async () => {
const response = await userApi.getUser(0);
const user = await response.json();
expect(response.ok()).toBeTruthy();
expect(user.name).toBeDefined();
});
},
);

Expand All @@ -29,14 +46,12 @@ test.describe("User API Tests", () => {
tag: TAGS.INTERNAL,
},
async () => {
const newUser: User = {
name: "John Doe",
email: "john@example.com",
};
const response = await userApi.createUser(newUser);
const user = await response.json();
expect(response.ok()).toBeTruthy();
expect(user.name).toBe("John Doe");
await test.step("Request users api to create a new user and validate results", async () => {
const response = await userApi.createUser(NEW_USER);
const user = await response.json();
expect(response.ok()).toBeTruthy();
expect(user.name).toBe("John Doe");
});
},
);
});
4 changes: 3 additions & 1 deletion src/tests/ui/fixtures/home-fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ export const test = base.extend<HomePageFixture>({
homePage: async ({ page }, use) => {
const url = process.env.PLAYWRIGHT_DEV_URL;
const homePage = new HomePage(page);
await homePage.navigate(url || "/");
await test.step("Navigate to the home page", async () => {
await homePage.navigate(url || "/");
});
await use(homePage);
},
});
Expand Down
4 changes: 3 additions & 1 deletion src/tests/ui/fixtures/todo-fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ export const test = base.extend<HomePageFixture>({
todoPage: async ({ page }, use) => {
const url = process.env.DEMO_PLAYWRIGHT_DEV_URL;
const todoPage = new TodoPage(page);
await todoPage.navigate(url || "/");
await test.step("Navigate to the todo page", async () => {
await todoPage.navigate(url || "/");
});
await use(todoPage);
},
});
Expand Down
Loading
Loading