Add k6 tests
This commit is contained in:
21
k6-example/LICENSE
Normal file
21
k6-example/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Sebastian Southern
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
40
k6-example/README.md
Normal file
40
k6-example/README.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# K6 Example
|
||||
|
||||
This repository contains a starting point for running k6 tests in typescript. It uses a publicly hosted endpoint so that anyone with internet access can test out the API themselves and then use this repo as a starting point to continue building their own tests.
|
||||
|
||||
## Pre-Reqs
|
||||
|
||||
- Download `k6` from here https://k6.io/docs/get-started/installation/
|
||||
- Install dependencies `yarn install`
|
||||
- Docker
|
||||
|
||||
### Installing xk6-dashboard with Docker
|
||||
|
||||
[xk6-dashboard](https://github.com/grafana/xk6-dashboard) is a k6 extension that can be used to visualise your performance test in real time.
|
||||
|
||||
To run the tests with monitoring with xk6-dashboard extension, we need to install it. The simplest way to install is via docker and can be done via
|
||||
|
||||
`docker pull ghcr.io/grafana/xk6-dashboard:0.6.1`
|
||||
|
||||
## Tests
|
||||
|
||||
### reqres
|
||||
|
||||
We use the [reqres](https://reqres.in/) publicly hosted REST API to showcase the testing with k6
|
||||
|
||||
To execute the first sample test that showcases how `per-vu-iterations` works, you can run:
|
||||
|
||||
`yarn test:demo`
|
||||
|
||||
To test with monitoring in place, run:
|
||||
|
||||
`yarn test-with-monitoring:demo`
|
||||
|
||||
To execute the second sample test that showcases how to use `stages`, you can run:
|
||||
|
||||
`yarn test:demo-stages`
|
||||
|
||||
To test with monitoring in place, run:
|
||||
|
||||
`yarn test-with-monitoring:demo-stages`
|
||||
|
||||
3
k6-example/configuration.yml
Normal file
3
k6-example/configuration.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
target:
|
||||
url: http://localhost
|
||||
port: 51001
|
||||
2208
k6-example/package-lock.json
generated
Normal file
2208
k6-example/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
29
k6-example/package.json
Normal file
29
k6-example/package.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "k6-example",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
"test:demo": "yarn build && k6 run dist/tests/load-tests.cjs",
|
||||
"test:demo-stages": "yarn build && k6 run dist/tests/reqres-stages.cjs",
|
||||
"test-with-monitoring:demo": "yarn build && docker run --platform linux/amd64 -it -p 5665:5665 -v $(pwd)/dist/:/src ghcr.io/grafana/xk6-dashboard:0.6.1 run --out 'dashboard=period=2s' /src/tests/reqres.cjs",
|
||||
"test-with-monitoring:demo-stages": "yarn build && docker run --platform linux/amd64 -it -p 5665:5665 -v $(pwd)/dist/:/src ghcr.io/grafana/xk6-dashboard:0.6.1 run --out 'dashboard=period=2s' /src/tests/reqres-stages.cjs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.23.3",
|
||||
"@rollup/plugin-babel": "^6.0.4",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/k6": "~0.47.3",
|
||||
"@types/node": "^24.5.2",
|
||||
"rollup-plugin-copy": "^3.5.0",
|
||||
"typescript": "5.3.2",
|
||||
"vite": "^5.0.0"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e",
|
||||
"dependencies": {
|
||||
"js-yaml": "^4.1.0"
|
||||
}
|
||||
}
|
||||
36
k6-example/src/apis/marketplace-apis.ts
Normal file
36
k6-example/src/apis/marketplace-apis.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import {Marketplace} from "../data/marketplace";
|
||||
import http from "k6/http";
|
||||
import {logWaitingTime} from "../utils/logger";
|
||||
import {Trend} from "k6/metrics";
|
||||
import {check} from "k6";
|
||||
import {Response} from "../data/common";
|
||||
|
||||
const marketplaceUrl = 'http://localhost:51001'
|
||||
|
||||
// Metrics that we want to track
|
||||
const metrics = {
|
||||
getMarketplace: new Trend("get_marketplace_time", true),
|
||||
};
|
||||
|
||||
export const getMarketplace = (): Response<Marketplace> => {
|
||||
const url = `${marketplaceUrl}/api/marketplace`;
|
||||
|
||||
const response = http.get(url);
|
||||
|
||||
// console.log('response:', response);
|
||||
|
||||
logWaitingTime({
|
||||
metric: metrics.getMarketplace,
|
||||
response: response,
|
||||
messageType: 'Get Marketplace',
|
||||
});
|
||||
|
||||
check(response, {
|
||||
'Get Marketplace: is 200': r => r.status === 200
|
||||
});
|
||||
|
||||
return {
|
||||
data: {} as Marketplace,
|
||||
statusCode: response.status
|
||||
}
|
||||
}
|
||||
113
k6-example/src/apis/reqres.ts
Normal file
113
k6-example/src/apis/reqres.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import { check } from "k6";
|
||||
import http, { StructuredRequestBody } from "k6/http";
|
||||
import { Trend } from "k6/metrics";
|
||||
import { logWaitingTime } from "../utils/logger";
|
||||
import { Response } from '../data/common';
|
||||
|
||||
export type User = {
|
||||
id: number;
|
||||
email: string;
|
||||
first_name: string;
|
||||
last_name: string;
|
||||
avatar: string;
|
||||
};
|
||||
|
||||
// Metrics that we want to track
|
||||
const metrics = {
|
||||
getUserResponseTime: new Trend("get_user_response_time", true),
|
||||
updateUserResponseTime: new Trend("update_user_response_time", true),
|
||||
deleteUserResponseTime: new Trend("delete_user_response_time", true),
|
||||
};
|
||||
|
||||
// Endpoint that we test against
|
||||
const reqResUrl = "https://reqres.in/api";
|
||||
|
||||
/**
|
||||
* getUser makes a GET request to the /users/:id endpoint and asserts
|
||||
* that the response is 200 and that the user id and email match the
|
||||
* user object that was passed in.
|
||||
* @param user
|
||||
* @returns Response<User>
|
||||
*/
|
||||
export const getUser = (user: User): Response<User> => {
|
||||
const url = reqResUrl;
|
||||
const params = {
|
||||
headers: {},
|
||||
};
|
||||
|
||||
const res = http.get(`${url}/users/${user.id}`, params);
|
||||
const jsonRes = res.json() as { data: User };
|
||||
logWaitingTime({
|
||||
metric: metrics.getUserResponseTime,
|
||||
response: res,
|
||||
messageType: `Get User`,
|
||||
});
|
||||
|
||||
check(res, {
|
||||
"Get User By Id: is 200": (r) => r.status === 200,
|
||||
"Get User By Id: has correct id": (_) => jsonRes.data.id === user.id,
|
||||
"Get User By Id: has correct email": (_) =>
|
||||
jsonRes.data.email === user.email,
|
||||
});
|
||||
|
||||
return {
|
||||
data: jsonRes.data,
|
||||
statusCode: res.status,
|
||||
};
|
||||
};
|
||||
|
||||
type UpdateUserResponse = {
|
||||
updatedAt: string;
|
||||
job: string;
|
||||
}
|
||||
|
||||
export const updateUser = (user: User): Response<UpdateUserResponse> => {
|
||||
const url = reqResUrl;
|
||||
const params = {
|
||||
headers: {},
|
||||
};
|
||||
const jobTitle = "QA Engineer";
|
||||
const payload: StructuredRequestBody = {
|
||||
job: jobTitle,
|
||||
};
|
||||
|
||||
|
||||
const res = http.patch(`${url}/users/${user.id}`, payload, params);
|
||||
const jsonRes = res.json() as UpdateUserResponse
|
||||
logWaitingTime({
|
||||
metric: metrics.updateUserResponseTime,
|
||||
response: res,
|
||||
messageType: `Update User`,
|
||||
});
|
||||
|
||||
check(res, {
|
||||
"Update User By Id: is 200": (r) => r.status === 200,
|
||||
"Update User By Id: has correct updatedAt": (_) => jsonRes.updatedAt !== "",
|
||||
"Update User By Id: has correct job title": (_) => jsonRes.job === jobTitle,
|
||||
});
|
||||
|
||||
return {
|
||||
data: jsonRes,
|
||||
statusCode: res.status,
|
||||
};
|
||||
};
|
||||
|
||||
export const deleteUser = (userId: number): Response<{}> => {
|
||||
const url = reqResUrl;
|
||||
|
||||
const res = http.del(`${url}/users/${userId}`);
|
||||
logWaitingTime({
|
||||
metric: metrics.deleteUserResponseTime,
|
||||
response: res,
|
||||
messageType: `Delete User`,
|
||||
});
|
||||
|
||||
check(res, {
|
||||
"Delete User By Id: is 204": (r) => r.status === 204,
|
||||
});
|
||||
|
||||
return {
|
||||
data: {},
|
||||
statusCode: res.status,
|
||||
};
|
||||
};
|
||||
4
k6-example/src/data/common.ts
Normal file
4
k6-example/src/data/common.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export type Response<T> = {
|
||||
data: T;
|
||||
statusCode: number;
|
||||
};
|
||||
15
k6-example/src/data/marketplace.ts
Normal file
15
k6-example/src/data/marketplace.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export interface Catalog {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface Item {
|
||||
id: string;
|
||||
name: string;
|
||||
isShared: boolean;
|
||||
}
|
||||
|
||||
export interface Marketplace {
|
||||
catalogs: Catalog[];
|
||||
sharedItems: Item[]
|
||||
}
|
||||
86
k6-example/src/data/users.ts
Normal file
86
k6-example/src/data/users.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
export const users = [
|
||||
{
|
||||
id: 1,
|
||||
email: "george.bluth@reqres.in",
|
||||
first_name: "George",
|
||||
last_name: "Bluth",
|
||||
avatar: "https://reqres.in/img/faces/1-image.jpg",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
email: "janet.weaver@reqres.in",
|
||||
first_name: "Janet",
|
||||
last_name: "Weaver",
|
||||
avatar: "https://reqres.in/img/faces/2-image.jpg",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
email: "emma.wong@reqres.in",
|
||||
first_name: "Emma",
|
||||
last_name: "Wong",
|
||||
avatar: "https://reqres.in/img/faces/3-image.jpg",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
email: "eve.holt@reqres.in",
|
||||
first_name: "Eve",
|
||||
last_name: "Holt",
|
||||
avatar: "https://reqres.in/img/faces/4-image.jpg",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
email: "charles.morris@reqres.in",
|
||||
first_name: "Charles",
|
||||
last_name: "Morris",
|
||||
avatar: "https://reqres.in/img/faces/5-image.jpg",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
email: "tracey.ramos@reqres.in",
|
||||
first_name: "Tracey",
|
||||
last_name: "Ramos",
|
||||
avatar: "https://reqres.in/img/faces/6-image.jpg",
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
email: "michael.lawson@reqres.in",
|
||||
first_name: "Michael",
|
||||
last_name: "Lawson",
|
||||
avatar: "https://reqres.in/img/faces/7-image.jpg",
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
email: "lindsay.ferguson@reqres.in",
|
||||
first_name: "Lindsay",
|
||||
last_name: "Ferguson",
|
||||
avatar: "https://reqres.in/img/faces/8-image.jpg",
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
email: "tobias.funke@reqres.in",
|
||||
first_name: "Tobias",
|
||||
last_name: "Funke",
|
||||
avatar: "https://reqres.in/img/faces/9-image.jpg",
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
email: "byron.fields@reqres.in",
|
||||
first_name: "Byron",
|
||||
last_name: "Fields",
|
||||
avatar: "https://reqres.in/img/faces/10-image.jpg",
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
email: "george.edwards@reqres.in",
|
||||
first_name: "George",
|
||||
last_name: "Edwards",
|
||||
avatar: "https://reqres.in/img/faces/11-image.jpg",
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
email: "rachel.howell@reqres.in",
|
||||
first_name: "Rachel",
|
||||
last_name: "Howell",
|
||||
avatar: "https://reqres.in/img/faces/12-image.jpg",
|
||||
},
|
||||
];
|
||||
3
k6-example/src/tests/config.ts
Normal file
3
k6-example/src/tests/config.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
|
||||
export const VUs = 1
|
||||
22
k6-example/src/tests/load-tests.ts
Normal file
22
k6-example/src/tests/load-tests.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
// @ts-ignore
|
||||
import { vu } from "k6/execution";
|
||||
import { Options } from "k6/options";
|
||||
import { logger } from "../utils/logger";
|
||||
import {getMarketplace} from "../apis/marketplace-apis";
|
||||
|
||||
export const options: Options = {
|
||||
scenarios: {
|
||||
login: {
|
||||
executor: 'per-vu-iterations',
|
||||
vus: 50,
|
||||
iterations: 10,
|
||||
maxDuration: '1h30m',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default function test () {
|
||||
logger.info(`Running iteration ${vu.iterationInInstance}`);
|
||||
|
||||
getMarketplace();
|
||||
}
|
||||
34
k6-example/src/tests/reqres-stages.ts
Normal file
34
k6-example/src/tests/reqres-stages.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { SharedArray } from "k6/data";
|
||||
import { vu } from "k6/execution";
|
||||
import { Options } from "k6/options";
|
||||
import { deleteUser, getUser, updateUser } from "../apis/reqres";
|
||||
import { users } from "../data/users";
|
||||
import { logger } from "../utils/logger";
|
||||
|
||||
const data = new SharedArray("users", function () {
|
||||
return users;
|
||||
});
|
||||
|
||||
export const options: Options = {
|
||||
stages: [
|
||||
// Ramp up to 12 users over 30 seconds
|
||||
{ duration: "1m", target: 12 },
|
||||
// Maintain steady state of 12 users over the next two minutes
|
||||
{ duration: "2m", target: 12 },
|
||||
// Ramp down to 0 users over the next 30 seconds
|
||||
{ duration: "30s", target: 0 },
|
||||
],
|
||||
};
|
||||
|
||||
export default function test() {
|
||||
// Get a random user from data that isn't currently being tested
|
||||
const user = data[vu.idInTest - 1];
|
||||
|
||||
logger.info(
|
||||
`Running iteration ${vu.iterationInInstance} for user id ${user.id} with name ${user.first_name} ${user.last_name}`
|
||||
);
|
||||
|
||||
getUser(user);
|
||||
updateUser(user);
|
||||
deleteUser(user.id);
|
||||
}
|
||||
35
k6-example/src/tests/reqres.ts
Normal file
35
k6-example/src/tests/reqres.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { SharedArray } from "k6/data";
|
||||
// @ts-ignore
|
||||
import { vu } from "k6/execution";
|
||||
import { Options } from "k6/options";
|
||||
import { deleteUser, getUser, updateUser } from "../apis/reqres";
|
||||
import { users } from "../data/users";
|
||||
import { logger } from "../utils/logger";
|
||||
|
||||
const data = new SharedArray("users", function () {
|
||||
return users;
|
||||
});
|
||||
|
||||
export const options: Options = {
|
||||
scenarios: {
|
||||
login: {
|
||||
executor: 'per-vu-iterations',
|
||||
vus: 5,
|
||||
iterations: 10,
|
||||
maxDuration: '1h30m',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default function test () {
|
||||
// Get a random user from data that isn't currently being tested
|
||||
const user = data[vu.idInTest - 1];
|
||||
|
||||
logger.info(
|
||||
`Running iteration ${vu.iterationInInstance} for user id ${user.id} with name ${user.first_name} ${user.last_name}`
|
||||
);
|
||||
|
||||
getUser(user);
|
||||
updateUser(user);
|
||||
deleteUser(user.id);
|
||||
}
|
||||
39
k6-example/src/utils/configuration.ts
Normal file
39
k6-example/src/utils/configuration.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import * as fs from 'fs';
|
||||
import * as yaml from 'js-yaml';
|
||||
import * as path from 'path';
|
||||
|
||||
const FILE_ENCODING = 'utf-8';
|
||||
const YAML_FILE_PATH = '../configuration.yml';
|
||||
|
||||
export interface Configuration {
|
||||
target: TargetConfiguration;
|
||||
}
|
||||
|
||||
export interface TargetConfiguration {
|
||||
url: string;
|
||||
port: string;
|
||||
}
|
||||
|
||||
|
||||
export function loadConfiguration(): Configuration {
|
||||
try {
|
||||
const fileContent = fs.readFileSync(path.resolve(__dirname, YAML_FILE_PATH), FILE_ENCODING);
|
||||
const data = yaml.load(fileContent);
|
||||
|
||||
return {
|
||||
target: {
|
||||
// @ts-ignore
|
||||
url: data['target']['url'],
|
||||
// @ts-ignore
|
||||
port: data['target']['port']
|
||||
}
|
||||
};
|
||||
} catch (e) {
|
||||
let error = {
|
||||
message: 'Error while loading the configuration.',
|
||||
cause: e
|
||||
};
|
||||
console.error(error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
49
k6-example/src/utils/logger.ts
Normal file
49
k6-example/src/utils/logger.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
export const getTimestamp = (): string => {
|
||||
const date = new Date();
|
||||
const hours = date.getHours().toString().padStart(2, "0");
|
||||
const minutes = date.getMinutes().toString().padStart(2, "0");
|
||||
const seconds = date.getSeconds().toString().padStart(2, "0");
|
||||
const milliseconds = date.getMilliseconds().toString().padStart(3, "0");
|
||||
|
||||
return `${hours}:${minutes}:${seconds}.${milliseconds}`;
|
||||
};
|
||||
|
||||
export const logger = {
|
||||
info(...val: any): void {
|
||||
console.log(getTimestamp(), ...val);
|
||||
},
|
||||
warn(...val: any): void {
|
||||
console.warn(getTimestamp(), ...val);
|
||||
},
|
||||
error(...val: any): void {
|
||||
console.error(getTimestamp(), ...val);
|
||||
},
|
||||
};
|
||||
|
||||
export const logWaitingTime = ({
|
||||
metric,
|
||||
response,
|
||||
messageType,
|
||||
}: {
|
||||
metric: any;
|
||||
response: any;
|
||||
messageType: string;
|
||||
}): void => {
|
||||
const responseTimeThreshold = 5000;
|
||||
let correlationId = "";
|
||||
let responseTime = response.timings.waiting;
|
||||
try {
|
||||
let json = response.json();
|
||||
correlationId = json.correlationId;
|
||||
} catch (err) {
|
||||
// noop
|
||||
}
|
||||
|
||||
// Log any responses that far longer than expected so we can troubleshoot those particular queries
|
||||
if (responseTime > responseTimeThreshold) {
|
||||
logger.warn(
|
||||
`${messageType} with correlationId '${correlationId}' took longer than ${responseTimeThreshold}`
|
||||
);
|
||||
}
|
||||
metric.add(responseTime);
|
||||
};
|
||||
26
k6-example/tsconfig.json
Normal file
26
k6-example/tsconfig.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"moduleResolution": "node",
|
||||
"module": "commonjs",
|
||||
"noEmit": true,
|
||||
"allowJs": true,
|
||||
"removeComments": false,
|
||||
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
|
||||
"skipLibCheck": true,
|
||||
}
|
||||
}
|
||||
67
k6-example/vite.config.js
Normal file
67
k6-example/vite.config.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import { babel } from '@rollup/plugin-babel';
|
||||
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
||||
import copy from 'rollup-plugin-copy';
|
||||
import fg from 'fast-glob';
|
||||
|
||||
/**
|
||||
* Gets the entrypoints of files within a set of given paths (i.e src/tests).
|
||||
* This is useful as when we execute k6, we run it against individual test files
|
||||
* @returns an object of [fileName]: absolute file path
|
||||
*/
|
||||
const getEntryPoints = (entryPoints) => {
|
||||
// Searches for files that match the patterns defined in the array of input points.
|
||||
// Returns an array of absolute file paths.
|
||||
const files = fg.sync(entryPoints, { absolute: true });
|
||||
|
||||
// Maps the file paths in the "files" array to an array of key-value pair.
|
||||
const entities = files.map((file) => {
|
||||
// Extract the part of the file path after the "src" folder and before the file extension.
|
||||
const [key] = file.match(/(?<=src\/).*$/) || [];
|
||||
|
||||
// Remove the file extension from the key.
|
||||
const keyWithoutExt = key.replace(/\.[^.]*$/, '');
|
||||
|
||||
return [keyWithoutExt, file];
|
||||
});
|
||||
|
||||
// Convert the array of key-value pairs to an object using the Object.fromEntries() method.
|
||||
// Returns an object where each key is the file name without the extension and the value is the absolute file path.
|
||||
return Object.fromEntries(entities);
|
||||
};
|
||||
|
||||
export default defineConfig({
|
||||
mode: 'production',
|
||||
build: {
|
||||
lib: {
|
||||
entry: getEntryPoints(['./src/tests/*.ts']),
|
||||
fileName: '[name]',
|
||||
formats: ['cjs'],
|
||||
sourcemap: true,
|
||||
},
|
||||
outDir: 'dist',
|
||||
minify: false, // Don't minimize, as it's not used in the browser
|
||||
rollupOptions: {
|
||||
external: [new RegExp(/^(k6|https?\:\/\/)(\/.*)?/)],
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts', '.js'],
|
||||
},
|
||||
plugins: [
|
||||
copy({
|
||||
targets: [
|
||||
{
|
||||
src: 'assets/**/*',
|
||||
dest: 'dist',
|
||||
noErrorOnMissing: true,
|
||||
},
|
||||
],
|
||||
}),
|
||||
babel({
|
||||
babelHelpers: 'bundled',
|
||||
exclude: /node_modules/,
|
||||
}),
|
||||
nodeResolve(),
|
||||
],
|
||||
});
|
||||
1160
k6-example/yarn.lock
Normal file
1160
k6-example/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user