Skip to content

Commit d80325d

Browse files
author
Vadim Yarosh
authored
PMM-11609 migrated generic cli tests to playwright (#711)
* PMM-11609 base migration * PMM-11609 initial migration * PMM-11609 fully migrated all tests. Failure are not resolved yet. * PMM-11609 fixed migrated tests * PMM-11609 revert package-lock.json * PMM-11609 added PMM-T1832 check * PMM-11609 fixed help test * PMM-11609 fixed packages merge
1 parent 8c33a70 commit d80325d

26 files changed

+1073
-479
lines changed

CONTRIBUTING.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,4 @@ This will help CodeceptJS to split tests for workers and parallel execution will
8585
### Annotations
8686

8787
* **Add a test to some Group if needed.**
88-
Add annotations for the test in the end on test Title. For ex. “Open Remote Instance Page and Add mysql instances @pmm-pre-update"
88+
Add annotations for the test in the end on test Title. For ex. “Open Remote Instance Page and Add mysql instances @pmm-pre-update"

cli/helpers/cli-helper.ts

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { test } from '@playwright/test';
2+
import ExecReturn from '@support/types/exec-return.class';
3+
import shell from 'shelljs';
4+
5+
/**
6+
* Shell(sh) echo().to() wrapper to use in tests with handy logs creation
7+
*
8+
* @param pathToFile path to the file including file name
9+
* @param content content {@code string} to insert as file content
10+
* @param stepTitle optional custom test step label
11+
*/
12+
export async function createFile(pathToFile: string, content: string, stepTitle: string | null = null) {
13+
const stepName = stepTitle || `Create "${pathToFile}" file with content:\n"${content}"`;
14+
await test.step(stepName, async () => {
15+
console.log(`echo: "${content}" >> ${pathToFile}`);
16+
shell.echo(content).to(pathToFile);
17+
});
18+
}
19+
20+
/**
21+
* Shell(sh) exec() wrapper to use outside of {@link test}
22+
* returns handy {@link ExecReturn} object.
23+
*
24+
* @param command sh command to execute
25+
* @return {@link ExecReturnClass} instance
26+
*/
27+
export function execute(command: string): ExecReturn {
28+
console.log(`exec: "${command}"`);
29+
const { stdout, stderr, code } = shell.exec(command.replace(/(\r\n|\n|\r)/gm, ''), { silent: false });
30+
if (stdout.length > 0) console.log(`Out: "${stdout}"`);
31+
if (stderr.length > 0) console.log(`Error: "${stderr}"`);
32+
return new ExecReturn(command, code, stdout, stderr);
33+
}
34+
35+
/**
36+
* Shell(sh) exec() wrapper to return handy {@link ExecReturn} object.
37+
*
38+
* @param command sh command to execute
39+
* @return {@link ExecReturnClass} instance
40+
*/
41+
export async function exec(command: string): Promise<ExecReturn> {
42+
return test.step(`Run "${command}" command`, async () => {
43+
return execute(command);
44+
});
45+
}
46+
47+
/**
48+
* Silent Shell(sh) exec() wrapper to return handy {@link ExecReturn} object.
49+
* Provides no logs to skip huge outputs.
50+
*
51+
* @param command sh command to execute
52+
* @return {@link ExecReturnClass} instance
53+
*/
54+
export async function execSilent(command: string): Promise<ExecReturn> {
55+
const { stdout, stderr, code } = await test.step(`Run "${command}" command`, async () => {
56+
return shell.exec(command.replace(/(\r\n|\n|\r)/gm, ''), { silent: false });
57+
});
58+
return new ExecReturn(command, code, stdout, stderr);
59+
}
60+
61+
/**
62+
* Scrape all metrics from exporter found by Service Name
63+
* **Note** will not work for versions earlier 2.29.0
64+
* as "port" was not included in pmm-admin output
65+
*
66+
* @param serviceName name of the service to search for the exporter
67+
* @param agentUser username to authenticate to exporter
68+
* @param agentPassword password for specified username to authenticate to exporter
69+
* @param dockerContainer Optional! docker container name to scrape metrics from
70+
*/
71+
export async function getMetrics(
72+
serviceName: string,
73+
agentUser: string,
74+
agentPassword: string,
75+
dockerContainer: string | null = null,
76+
): Promise<string> {
77+
const output = await test.step(
78+
`Scraping "${serviceName}" metrics${dockerContainer ? `in "${dockerContainer}" container` : ''}`,
79+
async () => {
80+
const prefix = dockerContainer ? `sudo docker exec ${dockerContainer} ` : '';
81+
const adminList = (await execute(`${prefix || 'sudo '}pmm-admin list`).assertSuccess()).getStdOutLines();
82+
const serviceId: string = adminList.find((item) => item.includes(serviceName))!
83+
.split(' ')
84+
.find((item: string) => item.includes('/service_id/'))!
85+
.trim() ?? '';
86+
87+
if (!serviceId) {
88+
throw new Error(`Failed to find '${serviceName}' service is in pmm-admin list output:\n${adminList}`);
89+
}
90+
91+
const listenPort = adminList.filter((item) => item.includes(serviceId))!
92+
.find((item: string) => item.includes('_exporter'))!
93+
.split(' ').filter((item) => item.trim().length > 0)
94+
.at(-1) ?? '';
95+
96+
if (!listenPort) {
97+
throw new Error(`Failed to find port of '${serviceId}' exporter is in pmm-admin list output:\n${adminList}`);
98+
}
99+
100+
return execute(`${prefix}curl -s "http://${agentUser}:${agentPassword}@127.0.0.1:${listenPort}/metrics"`);
101+
},
102+
);
103+
await output.assertSuccess();
104+
// TODO: parse into map(k => v) or json
105+
return output.stdout;
106+
}

cli/helpers/cliHelper.ts

-109
This file was deleted.

cli/helpers/custom-assertions.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { APIRequestContext, expect, request } from '@playwright/test';
2-
import * as cli from '@helpers/cliHelper';
2+
import * as cli from '@helpers/cli-helper';
33

44
export async function waitForPmmServerToBeReady(containerName: string, timeout = 60) {
55
await expect(async () => {

cli/helpers/zip-helper.ts

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import AdmZip from 'adm-zip';
2+
3+
export function readZipFile(pathToFile: string) {
4+
console.log(`Reading Zip File: "${pathToFile}"...`);
5+
return new AdmZip(pathToFile).getEntries().map(({ name }) => name);
6+
}
7+
8+
export function readFileFromZipArchive(zipPath: string, filePath: string) {
9+
return new AdmZip(zipPath).readFile(filePath);
10+
}
11+
12+
// export function seeEntriesInZip(filepath: string, entriesArray: any[]) {
13+
// const entries = readZipFile(filepath);
14+
// entriesArray.forEach((entry: string) => {
15+
// assert.ok(entries.includes(entry));
16+
// });
17+
// }

cli/package-lock.json

+19
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"dependencies": {
3030
"@faker-js/faker": "^8.3.1",
3131
"@types/luxon": "^3.3.5",
32+
"adm-zip": "^0.5.10",
3233
"axios": "^1.6.2",
3334
"luxon": "^3.4.4",
3435
"playwright": "^1.40.0",
@@ -40,6 +41,7 @@
4041
"yaml": "^2.3.4"
4142
},
4243
"devDependencies": {
44+
"@types/adm-zip": "^0.5.4",
4345
"@types/node": "^20.9.4",
4446
"@types/shelljs": "^0.8.15",
4547
"@typescript-eslint/eslint-plugin": "^6.12.0",

0 commit comments

Comments
 (0)