Skip to content

Commit

Permalink
test: performance test with k6 (#480)
Browse files Browse the repository at this point in the history
  • Loading branch information
tormoseng authored Jan 20, 2025
1 parent 18f79ba commit 9a732ec
Show file tree
Hide file tree
Showing 24 changed files with 723 additions and 7 deletions.
4 changes: 4 additions & 0 deletions e2e-tests/k6/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
screenshots
logs
./k6
36 changes: 36 additions & 0 deletions e2e-tests/k6/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Performance tests

The tests are written in TypeScript and run by the k6
test tool (https://k6.io/docs/) within a browser context.

### Run modes

- Functional test: 1 user and 1 iteration
- Performance test: X iterations over Y users

See `scenario/scenario.ts` for details.

### Commands

Install k6 and the xk6-extension to write to file (error log in `/logs`). On Mac (assume Go is installed along with `GOPATH` is set on `PATH`):
```bash
$ brew install k6
$ go install go.k6.io/xk6/cmd/xk6@latest
$ xk6 build v0.54.0 --with github.com/avitalique/xk6-file@latest
$ yarn install
```

Run functional test

```bash
# Headless
e2e-tests/k6$ ./k6 run --compatibility-mode=experimental_enhanced runTest.ts -e env=[dev | staging | prod]
# With browser
e2e-tests/k6$ K6_BROWSER_HEADLESS=false ./k6 run --compatibility-mode=experimental_enhanced runTest.ts -e env=[dev | staging | prod]
```

Run performance test - default: 100 iterations with 10 users

```bash
e2e-tests/k6$ ./k6 run --compatibility-mode=experimental_enhanced runTest.ts -e env=[dev | staging | prod] -e performanceTest=true
```
27 changes: 27 additions & 0 deletions e2e-tests/k6/conf/conf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { env } from './env.ts';
import { funcOptions, perfOptions } from './options.ts';
import { Options } from 'k6/options';
import { functScenario, perfScenario } from '../scenario/scenario.ts';
import { Metrics } from '../measurements/metrics.ts';

class Conf {
/* @ts-ignore */
host: string = __ENV.host || env.environments[__ENV.env || 'dev'].host;

/* @ts-ignore */
options: Options =
__ENV.performanceTest === 'true' ? perfOptions : funcOptions;

/* @ts-ignore */
isPerformanceTest: boolean = __ENV.performanceTest === 'true';

/* @ts-ignore */
usecase = (metrics: Metrics): Promise<void> => {
__ENV.performanceTest === 'true'
? perfScenario(metrics)
: functScenario(metrics);
};
}

// eslint-disable-next-line import/no-anonymous-default-export
export default new Conf();
15 changes: 15 additions & 0 deletions e2e-tests/k6/conf/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { EnvType } from '../types';

export const env: EnvType = {
environments: {
dev: {
host: 'http://localhost:3000',
},
staging: {
host: 'https://atb-staging.planner-web.mittatb.no',
},
prod: {
host: 'https://atb.planner-web.mittatb.no',
},
},
};
35 changes: 35 additions & 0 deletions e2e-tests/k6/conf/options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Options } from 'k6/options';

export const funcOptions: Options = {
summaryTrendStats: ['avg', 'min', 'med', 'max', 'p(95)', 'p(99)', 'count'],
scenarios: {
ui: {
executor: 'shared-iterations',
vus: 1,
iterations: 1,
options: {
browser: {
type: 'chromium',
},
},
},
},
thresholds: {},
};

export const perfOptions: Options = {
summaryTrendStats: ['avg', 'min', 'med', 'max', 'p(95)', 'p(99)', 'count'],
scenarios: {
ui: {
executor: 'shared-iterations',
vus: 10,
iterations: 100,
options: {
browser: {
type: 'chromium',
},
},
},
},
thresholds: {},
};
77 changes: 77 additions & 0 deletions e2e-tests/k6/data/locations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { FromLocationType, ToLocationType } from '../types';

export const getFromLocation = (): FromLocationType => {
return fromLocations[Math.floor(Math.random() * fromLocations.length)];
};

export const getFromLocationName = (): string => {
return fromLocations[Math.floor(Math.random() * fromLocations.length)].name;
};

export const getToLocationName = (): string => {
return toLocations[Math.floor(Math.random() * toLocations.length)].name;
};

export const getToLocationRegionName = (): string => {
return toLocationsRegion[Math.floor(Math.random() * toLocationsRegion.length)]
.name;
};

const fromLocations: FromLocationType[] = [
{
name: 'Olav Tryggvasons gate',
quay: '1',
},
{
name: 'Prinsens gate',
quay: 'P1',
},
{
name: 'Klettkrysset',
quay: '1',
},
{
name: 'Dronningens gate',
quay: 'D1',
},
{
name: 'Tillerterminalen',
quay: '1',
},
];

const toLocations: ToLocationType[] = [
{
name: 'Melhus sentrum',
},
{
name: 'Stjørdal stasjon',
},
{
name: 'Husebytunet',
},
{
name: 'Lade idrettsanlegg',
},
{
name: 'Ranheim idrettsplass',
},
];

const toLocationsRegion: ToLocationType[] = [
{
name: 'Namsos skysstasjon',
},
{
name: 'Meråker sentrum',
},
{
name: 'Hitra idrettspark',
},
{
name: 'Berkåk sentrum',
},
{
name: 'Selbu skysstasjon',
},
];
126 changes: 126 additions & 0 deletions e2e-tests/k6/measurements/measures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { Page } from 'k6/browser';

export class Measures {
private page: Page;

constructor(page: Page) {
this.page = page;
}

async mark(label: string) {
switch (label) {
case 'search':
await this.page.evaluate(() => {
window.performance.mark('search');
});
break;
case 'search-firstResult':
await this.page.evaluate(() => {
window.performance.mark('search-firstResult');
});
break;
case 'search-lastResult':
await this.page.evaluate(() => {
window.performance.mark('search-lastResult');
});
break;
case 'assistant-details-open':
await this.page.evaluate(() => {
window.performance.mark('assistant-details-open');
});
break;
case 'assistant-details-opened':
await this.page.evaluate(() => {
window.performance.mark('assistant-details-opened');
});
break;
case 'departures-details-open':
await this.page.evaluate(() => {
window.performance.mark('departures-details-open');
});
break;
case 'departures-details-opened':
await this.page.evaluate(() => {
window.performance.mark('departures-details-opened');
});
break;
}
}

async measure(label: string) {
switch (label) {
case 'measure-search-firstResult':
await this.page.evaluate(() =>
window.performance.measure(
'measure-search-firstResult',
'search',
'search-firstResult',
),
);
return await this.page.evaluate(
() =>
JSON.parse(
JSON.stringify(
window.performance.getEntriesByName(
'measure-search-firstResult',
),
),
)[0].duration,
);
case 'measure-search-lastResult':
await this.page.evaluate(() =>
window.performance.measure(
'measure-search-lastResult',
'search',
'search-lastResult',
),
);
return await this.page.evaluate(
() =>
JSON.parse(
JSON.stringify(
window.performance.getEntriesByName(
'measure-search-lastResult',
),
),
)[0].duration,
);
case 'measure-assistant-details-open':
await this.page.evaluate(() =>
window.performance.measure(
'measure-assistant-details-open',
'assistant-details-open',
'assistant-details-opened',
),
);
return await this.page.evaluate(
() =>
JSON.parse(
JSON.stringify(
window.performance.getEntriesByName(
'measure-assistant-details-open',
),
),
)[0].duration,
);
case 'measure-departures-details-open':
await this.page.evaluate(() =>
window.performance.measure(
'measure-departures-details-open',
'departures-details-open',
'departures-details-opened',
),
);
return await this.page.evaluate(
() =>
JSON.parse(
JSON.stringify(
window.performance.getEntriesByName(
'measure-departures-details-open',
),
),
)[0].duration,
);
}
}
}
Loading

0 comments on commit 9a732ec

Please sign in to comment.