@element-hq/element-web-playwright-common 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +9 -0
- package/README.md +28 -0
- package/lib/element-web-test.d.ts +26 -0
- package/lib/element-web-test.d.ts.map +1 -0
- package/lib/element-web-test.js +54 -0
- package/lib/expect/axe.d.ts +11 -0
- package/lib/expect/axe.d.ts.map +1 -0
- package/lib/expect/axe.js +22 -0
- package/lib/expect/index.d.ts +6 -0
- package/lib/expect/index.d.ts.map +1 -0
- package/lib/expect/index.js +10 -0
- package/lib/expect/screenshot.d.ts +14 -0
- package/lib/expect/screenshot.d.ts.map +1 -0
- package/lib/expect/screenshot.js +48 -0
- package/lib/expect/toHaveNoViolations.d.ts +11 -0
- package/lib/expect/toHaveNoViolations.d.ts.map +1 -0
- package/lib/expect/toHaveNoViolations.js +22 -0
- package/lib/expect/toMatchScreenshot.d.ts +13 -0
- package/lib/expect/toMatchScreenshot.d.ts.map +1 -0
- package/lib/expect/toMatchScreenshot.js +44 -0
- package/lib/expect/toPassAxeCheck.d.ts +7 -0
- package/lib/expect/toPassAxeCheck.d.ts.map +1 -0
- package/lib/expect/toPassAxeCheck.js +22 -0
- package/lib/fixtures/axe.d.ts +8 -0
- package/lib/fixtures/axe.d.ts.map +1 -0
- package/lib/fixtures/axe.js +15 -0
- package/lib/fixtures/index.d.ts +10 -0
- package/lib/fixtures/index.d.ts.map +1 -0
- package/lib/fixtures/index.js +10 -0
- package/lib/fixtures/services.d.ts +57 -0
- package/lib/fixtures/services.d.ts.map +1 -0
- package/lib/fixtures/services.js +114 -0
- package/lib/fixtures/user.d.ts +31 -0
- package/lib/fixtures/user.d.ts.map +1 -0
- package/lib/fixtures/user.js +47 -0
- package/lib/index.d.ts +35 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +54 -0
- package/lib/logger.d.ts +10 -0
- package/lib/logger.d.ts.map +1 -0
- package/lib/logger.js +59 -0
- package/lib/playwright.d.ts +2 -0
- package/lib/playwright.d.ts.map +1 -0
- package/lib/playwright.js +1 -0
- package/lib/testcontainers/HomeserverContainer.d.ts +70 -0
- package/lib/testcontainers/HomeserverContainer.d.ts.map +1 -0
- package/lib/testcontainers/HomeserverContainer.js +7 -0
- package/lib/testcontainers/index.d.ts +5 -0
- package/lib/testcontainers/index.d.ts.map +1 -0
- package/lib/testcontainers/index.js +9 -0
- package/lib/testcontainers/mailpit.d.ts +33 -0
- package/lib/testcontainers/mailpit.d.ts.map +1 -0
- package/lib/testcontainers/mailpit.js +52 -0
- package/lib/testcontainers/mas.d.ts +182 -0
- package/lib/testcontainers/mas.d.ts.map +1 -0
- package/lib/testcontainers/mas.js +311 -0
- package/lib/testcontainers/synapse.d.ts +229 -0
- package/lib/testcontainers/synapse.d.ts.map +1 -0
- package/lib/testcontainers/synapse.js +383 -0
- package/lib/utils/api.d.ts +49 -0
- package/lib/utils/api.d.ts.map +1 -0
- package/lib/utils/api.js +73 -0
- package/lib/utils/logger.d.ts +25 -0
- package/lib/utils/logger.d.ts.map +1 -0
- package/lib/utils/logger.js +74 -0
- package/lib/utils/object.d.ts +8 -0
- package/lib/utils/object.d.ts.map +1 -0
- package/lib/utils/object.js +15 -0
- package/lib/utils/port.d.ts +5 -0
- package/lib/utils/port.d.ts.map +1 -0
- package/lib/utils/port.js +20 -0
- package/lib/utils/rand.d.ts +6 -0
- package/lib/utils/rand.d.ts.map +1 -0
- package/lib/utils/rand.js +15 -0
- package/package.json +30 -0
- package/playwright-screenshots.sh +53 -0
- package/src/@types/playwright-core.d.ts +12 -0
- package/src/expect/axe.ts +37 -0
- package/src/expect/index.ts +21 -0
- package/src/expect/screenshot.ts +79 -0
- package/src/fixtures/axe.ts +22 -0
- package/src/fixtures/index.ts +15 -0
- package/src/fixtures/services.ts +188 -0
- package/src/fixtures/user.ts +93 -0
- package/src/index.ts +92 -0
- package/src/testcontainers/HomeserverContainer.ts +87 -0
- package/src/testcontainers/index.ts +15 -0
- package/src/testcontainers/mailpit.ts +62 -0
- package/src/testcontainers/mas.ts +382 -0
- package/src/testcontainers/synapse.ts +493 -0
- package/src/utils/api.ts +113 -0
- package/src/utils/logger.ts +79 -0
- package/src/utils/object.ts +16 -0
- package/src/utils/port.ts +22 -0
- package/src/utils/rand.ts +17 -0
- package/tsconfig.json +10 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type Page } from "@playwright/test";
|
|
2
|
+
import { Credentials } from "../utils/api.js";
|
|
3
|
+
export declare const test: import("playwright/test").TestType<import("playwright/test").PlaywrightTestArgs & import("playwright/test").PlaywrightTestOptions & import("./services.js").TestFixtures & {
|
|
4
|
+
/**
|
|
5
|
+
* The displayname to use for the user registered in {@link #credentials}.
|
|
6
|
+
*
|
|
7
|
+
* To set it, call `test.use({ displayName: "myDisplayName" })` in the test file or `describe` block.
|
|
8
|
+
* See {@link https://playwright.dev/docs/api/class-test#test-use}.
|
|
9
|
+
*/
|
|
10
|
+
displayName?: string;
|
|
11
|
+
/**
|
|
12
|
+
* A test fixture which registers a test user on the {@link #homeserver} and supplies the details
|
|
13
|
+
* of the registered user.
|
|
14
|
+
*/
|
|
15
|
+
credentials: Credentials;
|
|
16
|
+
/**
|
|
17
|
+
* The same as {@link https://playwright.dev/docs/api/class-fixtures#fixtures-page|`page`},
|
|
18
|
+
* but adds an initScript which will populate localStorage with the user's details from
|
|
19
|
+
* {@link #credentials} and {@link #homeserver}.
|
|
20
|
+
*
|
|
21
|
+
* Similar to {@link #user}, but doesn't load the app.
|
|
22
|
+
*/
|
|
23
|
+
pageWithCredentials: Page;
|
|
24
|
+
/**
|
|
25
|
+
* A (rather poorly-named) test fixture which registers a user per {@link #credentials}, stores
|
|
26
|
+
* the credentials into localStorage per {@link #homeserver}, and then loads the front page of the
|
|
27
|
+
* app.
|
|
28
|
+
*/
|
|
29
|
+
user: Credentials;
|
|
30
|
+
}, import("playwright/test").PlaywrightWorkerArgs & import("playwright/test").PlaywrightWorkerOptions & import("./services.js").WorkerOptions & import("./services.js").Services>;
|
|
31
|
+
//# sourceMappingURL=user.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../src/fixtures/user.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAI7C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,eAAO,MAAM,IAAI;IACb;;;;;OAKG;kBACW,MAAM;IAEpB;;;OAGG;iBACU,WAAW;IAExB;;;;;;OAMG;yBACkB,IAAI;IAEzB;;;;OAIG;UACG,WAAW;iLAiDnB,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2024-2025 New Vector Ltd.
|
|
3
|
+
Copyright 2023 The Matrix.org Foundation C.I.C.
|
|
4
|
+
|
|
5
|
+
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
6
|
+
Please see LICENSE files in the repository root for full details.
|
|
7
|
+
*/
|
|
8
|
+
import { sample, uniqueId } from "lodash-es";
|
|
9
|
+
import { test as base } from "./services.js";
|
|
10
|
+
export const test = base.extend({
|
|
11
|
+
displayName: undefined,
|
|
12
|
+
credentials: async ({ homeserver, displayName: testDisplayName }, use, testInfo) => {
|
|
13
|
+
const names = ["Alice", "Bob", "Charlie", "Daniel", "Eve", "Frank", "Grace", "Hannah", "Isaac", "Judy"];
|
|
14
|
+
const password = uniqueId("password_");
|
|
15
|
+
const displayName = testDisplayName ?? sample(names);
|
|
16
|
+
const credentials = await homeserver.registerUser(`user_${testInfo.testId}`, password, displayName);
|
|
17
|
+
console.log(`Registered test user ${credentials.userId} with displayname ${displayName}`);
|
|
18
|
+
await use({
|
|
19
|
+
...credentials,
|
|
20
|
+
displayName,
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
pageWithCredentials: async ({ page, homeserver, credentials }, use) => {
|
|
24
|
+
await page.addInitScript(({ baseUrl, credentials }) => {
|
|
25
|
+
// Seed the localStorage with the required credentials
|
|
26
|
+
window.localStorage.setItem("mx_hs_url", baseUrl);
|
|
27
|
+
window.localStorage.setItem("mx_user_id", credentials.userId);
|
|
28
|
+
window.localStorage.setItem("mx_access_token", credentials.accessToken);
|
|
29
|
+
window.localStorage.setItem("mx_device_id", credentials.deviceId);
|
|
30
|
+
window.localStorage.setItem("mx_is_guest", "false");
|
|
31
|
+
window.localStorage.setItem("mx_has_pickle_key", "false");
|
|
32
|
+
window.localStorage.setItem("mx_has_access_token", "true");
|
|
33
|
+
window.localStorage.setItem("mx_local_settings", JSON.stringify({
|
|
34
|
+
// Retain any other settings which may have already been set
|
|
35
|
+
...JSON.parse(window.localStorage.getItem("mx_local_settings") || "{}"),
|
|
36
|
+
// Ensure the language is set to a consistent value
|
|
37
|
+
language: "en",
|
|
38
|
+
}));
|
|
39
|
+
}, { baseUrl: homeserver.baseUrl, credentials });
|
|
40
|
+
await use(page);
|
|
41
|
+
},
|
|
42
|
+
user: async ({ pageWithCredentials: page, credentials }, use) => {
|
|
43
|
+
await page.goto("/");
|
|
44
|
+
await page.waitForSelector(".mx_MatrixChat", { timeout: 30000 });
|
|
45
|
+
await use(credentials);
|
|
46
|
+
},
|
|
47
|
+
});
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type Config as BaseConfig } from "@element-hq/element-web-module-api";
|
|
2
|
+
export interface Config extends BaseConfig {
|
|
3
|
+
default_server_config: {
|
|
4
|
+
"m.homeserver"?: {
|
|
5
|
+
base_url: string;
|
|
6
|
+
server_name?: string;
|
|
7
|
+
};
|
|
8
|
+
"m.identity_server"?: {
|
|
9
|
+
base_url: string;
|
|
10
|
+
server_name?: string;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
setting_defaults: Record<string, unknown>;
|
|
14
|
+
map_style_url?: string;
|
|
15
|
+
features: Record<string, boolean>;
|
|
16
|
+
modules?: string[];
|
|
17
|
+
}
|
|
18
|
+
export declare const CONFIG_JSON: Partial<Config>;
|
|
19
|
+
export interface TestFixtures {
|
|
20
|
+
/**
|
|
21
|
+
* The contents of the config.json to send when the client requests it.
|
|
22
|
+
*/
|
|
23
|
+
config: Partial<typeof CONFIG_JSON>;
|
|
24
|
+
labsFlags: string[];
|
|
25
|
+
}
|
|
26
|
+
export declare const test: import("playwright/test").TestType<import("playwright/test").PlaywrightTestArgs & import("playwright/test").PlaywrightTestOptions & {
|
|
27
|
+
axe: import("@axe-core/playwright").AxeBuilder;
|
|
28
|
+
} & import("./fixtures/services.js").TestFixtures & {
|
|
29
|
+
displayName?: string;
|
|
30
|
+
credentials: import("./utils/api.js").Credentials;
|
|
31
|
+
pageWithCredentials: import("playwright-core").Page;
|
|
32
|
+
user: import("./utils/api.js").Credentials;
|
|
33
|
+
} & TestFixtures, import("playwright/test").PlaywrightWorkerArgs & import("playwright/test").PlaywrightWorkerOptions & import("./fixtures/services.js").WorkerOptions & import("./fixtures/services.js").Services>;
|
|
34
|
+
export { expect, type ToMatchScreenshotOptions } from "./expect/index.js";
|
|
35
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,MAAM,IAAI,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAU/E,MAAM,WAAW,MAAO,SAAQ,UAAU;IACtC,qBAAqB,EAAE;QACnB,cAAc,CAAC,EAAE;YACb,QAAQ,EAAE,MAAM,CAAC;YACjB,WAAW,CAAC,EAAE,MAAM,CAAC;SACxB,CAAC;QACF,mBAAmB,CAAC,EAAE;YAClB,QAAQ,EAAE,MAAM,CAAC;YACjB,WAAW,CAAC,EAAE,MAAM,CAAC;SACxB,CAAC;KACL,CAAC;IACF,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAGD,eAAO,MAAM,WAAW,EAAE,OAAO,CAAC,MAAM,CAevC,CAAC;AAEF,MAAM,WAAW,YAAY;IACzB;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC,OAAO,WAAW,CAAC,CAAC;IAEpC,SAAS,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,eAAO,MAAM,IAAI;;;;;;;kNA2Bf,CAAC;AAEH,OAAO,EAAE,MAAM,EAAE,KAAK,wBAAwB,EAAE,MAAM,mBAAmB,CAAC"}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2024-2025 New Vector Ltd.
|
|
3
|
+
Copyright 2023 The Matrix.org Foundation C.I.C.
|
|
4
|
+
|
|
5
|
+
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
6
|
+
Please see LICENSE files in the repository root for full details.
|
|
7
|
+
*/
|
|
8
|
+
import { test as base } from "./fixtures/index.js";
|
|
9
|
+
// Enable experimental service worker support
|
|
10
|
+
// See https://playwright.dev/docs/service-workers-experimental#how-to-enable
|
|
11
|
+
process.env["PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS"] = "1";
|
|
12
|
+
// This is deliberately quite a minimal config.json, so that we can test that the default settings actually work.
|
|
13
|
+
export const CONFIG_JSON = {
|
|
14
|
+
default_server_config: {},
|
|
15
|
+
// The default language is set here for test consistency
|
|
16
|
+
setting_defaults: {
|
|
17
|
+
language: "en-GB",
|
|
18
|
+
},
|
|
19
|
+
// the location tests want a map style url.
|
|
20
|
+
map_style_url: "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx",
|
|
21
|
+
features: {
|
|
22
|
+
// We don't want to go through the feature announcement during the e2e test
|
|
23
|
+
feature_release_announcement: false,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
export const test = base.extend({
|
|
27
|
+
config: {}, // We merge this atop the default CONFIG_JSON in the page fixture to make extending it easier
|
|
28
|
+
labsFlags: [],
|
|
29
|
+
page: async ({ homeserver, context, page, config, labsFlags }, use) => {
|
|
30
|
+
await context.route(`http://localhost:8080/config.json*`, async (route) => {
|
|
31
|
+
const json = {
|
|
32
|
+
...CONFIG_JSON,
|
|
33
|
+
...config,
|
|
34
|
+
default_server_config: {
|
|
35
|
+
"m.homeserver": {
|
|
36
|
+
base_url: homeserver.baseUrl,
|
|
37
|
+
},
|
|
38
|
+
...config.default_server_config,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
json["features"] = {
|
|
42
|
+
...json["features"],
|
|
43
|
+
// Enable the lab features
|
|
44
|
+
...labsFlags.reduce((obj, flag) => {
|
|
45
|
+
obj[flag] = true;
|
|
46
|
+
return obj;
|
|
47
|
+
}, {}),
|
|
48
|
+
};
|
|
49
|
+
await route.fulfill({ json });
|
|
50
|
+
});
|
|
51
|
+
await use(page);
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
export { expect } from "./expect/index.js";
|
package/lib/logger.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type BrowserContext, type TestInfo } from "@playwright/test";
|
|
2
|
+
import { type Readable } from "stream";
|
|
3
|
+
export declare class Logger {
|
|
4
|
+
private pages;
|
|
5
|
+
private logs;
|
|
6
|
+
getConsumer(container: string): (stream: Readable) => void;
|
|
7
|
+
onTestStarted(context: BrowserContext): Promise<void>;
|
|
8
|
+
onTestFinished(testInfo: TestInfo): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,cAAc,EAAa,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjF,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAGvC,qBAAa,MAAM;IACf,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,IAAI,CAA8B;IAEnC,WAAW,CAAC,SAAS,EAAE,MAAM,IAExB,QAAQ,QAAQ;IAUf,aAAa,CAAC,OAAO,EAAE,cAAc;IAyBrC,cAAc,CAAC,QAAQ,EAAE,QAAQ;CAWjD"}
|
package/lib/logger.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2024 New Vector Ltd.
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
|
5
|
+
Please see LICENSE files in the repository root for full details.
|
|
6
|
+
*/
|
|
7
|
+
import stripAnsi from "strip-ansi";
|
|
8
|
+
export class Logger {
|
|
9
|
+
pages = [];
|
|
10
|
+
logs = {};
|
|
11
|
+
getConsumer(container) {
|
|
12
|
+
this.logs[container] = "";
|
|
13
|
+
return (stream) => {
|
|
14
|
+
stream.on("data", (chunk) => {
|
|
15
|
+
this.logs[container] += chunk.toString();
|
|
16
|
+
});
|
|
17
|
+
stream.on("err", (chunk) => {
|
|
18
|
+
this.logs[container] += "ERR " + chunk.toString();
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
async onTestStarted(context) {
|
|
23
|
+
this.pages = [];
|
|
24
|
+
for (const id in this.logs) {
|
|
25
|
+
if (id.startsWith("page-")) {
|
|
26
|
+
delete this.logs[id];
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
this.logs[id] = "";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
context.on("console", (msg) => {
|
|
33
|
+
const page = msg.page();
|
|
34
|
+
if (!page)
|
|
35
|
+
return;
|
|
36
|
+
let pageIdx = this.pages.indexOf(page);
|
|
37
|
+
if (pageIdx === -1) {
|
|
38
|
+
this.pages.push(page);
|
|
39
|
+
pageIdx = this.pages.length - 1;
|
|
40
|
+
this.logs[`page-${pageIdx}`] = `Console logs for page with URL: ${page.url()}\n\n`;
|
|
41
|
+
}
|
|
42
|
+
const type = msg.type();
|
|
43
|
+
const text = msg.text();
|
|
44
|
+
this.logs[`page-${pageIdx}`] += `${type}: ${text}\n`;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
async onTestFinished(testInfo) {
|
|
48
|
+
if (testInfo.status !== "passed") {
|
|
49
|
+
for (const id in this.logs) {
|
|
50
|
+
if (!this.logs[id])
|
|
51
|
+
continue;
|
|
52
|
+
await testInfo.attach(id, {
|
|
53
|
+
body: stripAnsi(this.logs[id]),
|
|
54
|
+
contentType: "text/plain",
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playwright.d.ts","sourceRoot":"","sources":["../src/playwright.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "@playwright/test";
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { type AbstractStartedContainer, type GenericContainer } from "testcontainers";
|
|
2
|
+
import { type APIRequestContext, type TestInfo } from "@playwright/test";
|
|
3
|
+
import { type StartedMatrixAuthenticationServiceContainer } from "./mas";
|
|
4
|
+
import { ClientServerApi, Credentials } from "../utils/api";
|
|
5
|
+
import { StartedMailpitContainer } from "./mailpit";
|
|
6
|
+
export interface HomeserverInstance {
|
|
7
|
+
readonly baseUrl: string;
|
|
8
|
+
readonly csApi: ClientServerApi;
|
|
9
|
+
/**
|
|
10
|
+
* Register a user on the given Homeserver using the shared registration secret.
|
|
11
|
+
* @param username the username of the user to register
|
|
12
|
+
* @param password the password of the user to register
|
|
13
|
+
* @param displayName optional display name to set on the newly registered user
|
|
14
|
+
*/
|
|
15
|
+
registerUser(username: string, password: string, displayName?: string): Promise<Credentials>;
|
|
16
|
+
/**
|
|
17
|
+
* Logs into synapse with the given username/password
|
|
18
|
+
* @param userId login username
|
|
19
|
+
* @param password login password
|
|
20
|
+
*/
|
|
21
|
+
loginUser(userId: string, password: string): Promise<Credentials>;
|
|
22
|
+
/**
|
|
23
|
+
* Sets a third party identifier for the given user. This only supports setting a single 3pid and will
|
|
24
|
+
* replace any others.
|
|
25
|
+
* @param userId The full ID of the user to edit (as returned from registerUser)
|
|
26
|
+
* @param medium The medium of the 3pid to set
|
|
27
|
+
* @param address The address of the 3pid to set
|
|
28
|
+
*/
|
|
29
|
+
setThreepid(userId: string, medium: string, address: string): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
export interface HomeserverContainer<Config> extends GenericContainer {
|
|
32
|
+
/**
|
|
33
|
+
* Set a configuration field in the config
|
|
34
|
+
* @param key - the key to set
|
|
35
|
+
* @param value - the value to set
|
|
36
|
+
*/
|
|
37
|
+
withConfigField<Key extends keyof Config>(key: Key, value: Config[Key]): this;
|
|
38
|
+
/**
|
|
39
|
+
* Merge a partial configuration into the config
|
|
40
|
+
* @param config - the partial configuration to merge
|
|
41
|
+
*/
|
|
42
|
+
withConfig(config: Partial<Config>): this;
|
|
43
|
+
/**
|
|
44
|
+
* Set the SMTP server to use for sending emails
|
|
45
|
+
* @param mailpit - the mailpit container to use
|
|
46
|
+
*/
|
|
47
|
+
withSmtpServer(mailpit: StartedMailpitContainer): this;
|
|
48
|
+
/**
|
|
49
|
+
* Set the MAS server to use for delegated auth
|
|
50
|
+
* @param mas - the MAS container to use
|
|
51
|
+
*/
|
|
52
|
+
withMatrixAuthenticationService(mas?: StartedMatrixAuthenticationServiceContainer): this;
|
|
53
|
+
/**
|
|
54
|
+
* Start the container
|
|
55
|
+
*/
|
|
56
|
+
start(): Promise<StartedHomeserverContainer>;
|
|
57
|
+
}
|
|
58
|
+
export interface StartedHomeserverContainer extends AbstractStartedContainer, HomeserverInstance {
|
|
59
|
+
/**
|
|
60
|
+
* Set the request context for the APIs
|
|
61
|
+
* @param request - the request context to set
|
|
62
|
+
*/
|
|
63
|
+
setRequest(request: APIRequestContext): void;
|
|
64
|
+
/**
|
|
65
|
+
* Clean up the server to prevent rooms leaking between tests
|
|
66
|
+
* @param testInfo - the test info for the test that just finished
|
|
67
|
+
*/
|
|
68
|
+
onTestFinished(testInfo: TestInfo): Promise<void>;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=HomeserverContainer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HomeserverContainer.d.ts","sourceRoot":"","sources":["../../src/testcontainers/HomeserverContainer.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,wBAAwB,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACtF,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEzE,OAAO,EAAE,KAAK,2CAA2C,EAAE,MAAM,OAAO,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AAEpD,MAAM,WAAW,kBAAkB;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAEhC;;;;;OAKG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAE7F;;;;OAIG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAElE;;;;;;OAMG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/E;AAED,MAAM,WAAW,mBAAmB,CAAC,MAAM,CAAE,SAAQ,gBAAgB;IACjE;;;;OAIG;IACH,eAAe,CAAC,GAAG,SAAS,MAAM,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;IAE9E;;;OAGG;IACH,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IAE1C;;;OAGG;IACH,cAAc,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI,CAAC;IACvD;;;OAGG;IACH,+BAA+B,CAAC,GAAG,CAAC,EAAE,2CAA2C,GAAG,IAAI,CAAC;IAEzF;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,0BAA2B,SAAQ,wBAAwB,EAAE,kBAAkB;IAC5F;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAE7C;;;OAGG;IACH,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type { HomeserverInstance, HomeserverContainer, StartedHomeserverContainer } from "./HomeserverContainer.js";
|
|
2
|
+
export { type SynapseConfig, SynapseContainer, StartedSynapseContainer } from "./synapse.js";
|
|
3
|
+
export { type MasConfig, MatrixAuthenticationServiceContainer, StartedMatrixAuthenticationServiceContainer, } from "./mas.js";
|
|
4
|
+
export { MailpitClient, MailpitContainer, StartedMailpitContainer } from "./mailpit.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testcontainers/index.ts"],"names":[],"mappings":"AAOA,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AACpH,OAAO,EAAE,KAAK,aAAa,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAC7F,OAAO,EACH,KAAK,SAAS,EACd,oCAAoC,EACpC,2CAA2C,GAC9C,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2025 New Vector Ltd.
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
5
|
+
Please see LICENSE files in the repository root for full details.
|
|
6
|
+
*/
|
|
7
|
+
export { SynapseContainer, StartedSynapseContainer } from "./synapse.js";
|
|
8
|
+
export { MatrixAuthenticationServiceContainer, StartedMatrixAuthenticationServiceContainer, } from "./mas.js";
|
|
9
|
+
export { MailpitContainer, StartedMailpitContainer } from "./mailpit.js";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { AbstractStartedContainer, GenericContainer, type StartedTestContainer } from "testcontainers";
|
|
2
|
+
import { MailpitClient } from "mailpit-api";
|
|
3
|
+
export type { MailpitClient };
|
|
4
|
+
/**
|
|
5
|
+
* A testcontainer for Mailpit.
|
|
6
|
+
*
|
|
7
|
+
* Exposes port 8025.
|
|
8
|
+
* Waits for listening ports.
|
|
9
|
+
* Disables SMTP authentication.
|
|
10
|
+
*/
|
|
11
|
+
export declare class MailpitContainer extends GenericContainer {
|
|
12
|
+
constructor();
|
|
13
|
+
/**
|
|
14
|
+
* Start the Mailpit container.
|
|
15
|
+
*/
|
|
16
|
+
start(): Promise<StartedMailpitContainer>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A started Mailpit container.
|
|
20
|
+
*/
|
|
21
|
+
export declare class StartedMailpitContainer extends AbstractStartedContainer {
|
|
22
|
+
readonly client: MailpitClient;
|
|
23
|
+
constructor(container: StartedTestContainer);
|
|
24
|
+
/**
|
|
25
|
+
* Get the hostname to use to connect to the Mailpit container from inside the docker network.
|
|
26
|
+
*/
|
|
27
|
+
get internalHost(): string;
|
|
28
|
+
/**
|
|
29
|
+
* Get the port to use to connect to the Mailpit container from inside the docker network.
|
|
30
|
+
*/
|
|
31
|
+
get internalSmtpPort(): number;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=mailpit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mailpit.d.ts","sourceRoot":"","sources":["../../src/testcontainers/mailpit.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,KAAK,oBAAoB,EAAQ,MAAM,gBAAgB,CAAC;AAC7G,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,YAAY,EAAE,aAAa,EAAE,CAAC;AAE9B;;;;;;GAMG;AACH,qBAAa,gBAAiB,SAAQ,gBAAgB;;IAUlD;;OAEG;IACmB,KAAK,IAAI,OAAO,CAAC,uBAAuB,CAAC;CAGlE;AAED;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,wBAAwB;IACjE,SAAgB,MAAM,EAAE,aAAa,CAAC;gBAEnB,SAAS,EAAE,oBAAoB;IAKlD;;OAEG;IACH,IAAW,YAAY,IAAI,MAAM,CAEhC;IAED;;OAEG;IACH,IAAW,gBAAgB,IAAI,MAAM,CAEpC;CACJ"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2024-2025 New Vector Ltd.
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|
5
|
+
Please see LICENSE files in the repository root for full details.
|
|
6
|
+
*/
|
|
7
|
+
import { AbstractStartedContainer, GenericContainer, Wait } from "testcontainers";
|
|
8
|
+
import { MailpitClient } from "mailpit-api";
|
|
9
|
+
/**
|
|
10
|
+
* A testcontainer for Mailpit.
|
|
11
|
+
*
|
|
12
|
+
* Exposes port 8025.
|
|
13
|
+
* Waits for listening ports.
|
|
14
|
+
* Disables SMTP authentication.
|
|
15
|
+
*/
|
|
16
|
+
export class MailpitContainer extends GenericContainer {
|
|
17
|
+
constructor() {
|
|
18
|
+
super("axllent/mailpit:latest");
|
|
19
|
+
this.withExposedPorts(8025).withWaitStrategy(Wait.forListeningPorts()).withEnvironment({
|
|
20
|
+
MP_SMTP_AUTH_ALLOW_INSECURE: "true",
|
|
21
|
+
MP_SMTP_AUTH_ACCEPT_ANY: "true",
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Start the Mailpit container.
|
|
26
|
+
*/
|
|
27
|
+
async start() {
|
|
28
|
+
return new StartedMailpitContainer(await super.start());
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A started Mailpit container.
|
|
33
|
+
*/
|
|
34
|
+
export class StartedMailpitContainer extends AbstractStartedContainer {
|
|
35
|
+
client;
|
|
36
|
+
constructor(container) {
|
|
37
|
+
super(container);
|
|
38
|
+
this.client = new MailpitClient(`http://${container.getHost()}:${container.getMappedPort(8025)}`);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get the hostname to use to connect to the Mailpit container from inside the docker network.
|
|
42
|
+
*/
|
|
43
|
+
get internalHost() {
|
|
44
|
+
return "mailpit";
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get the port to use to connect to the Mailpit container from inside the docker network.
|
|
48
|
+
*/
|
|
49
|
+
get internalSmtpPort() {
|
|
50
|
+
return 1025;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { AbstractStartedContainer, GenericContainer, type StartedTestContainer } from "testcontainers";
|
|
2
|
+
import { type StartedPostgreSqlContainer } from "@testcontainers/postgresql";
|
|
3
|
+
import { type Credentials } from "../utils/api.js";
|
|
4
|
+
declare const DEFAULT_CONFIG: {
|
|
5
|
+
http: {
|
|
6
|
+
listeners: {
|
|
7
|
+
name: string;
|
|
8
|
+
resources: ({
|
|
9
|
+
name: string;
|
|
10
|
+
playground?: undefined;
|
|
11
|
+
path?: undefined;
|
|
12
|
+
} | {
|
|
13
|
+
name: string;
|
|
14
|
+
playground: boolean;
|
|
15
|
+
path?: undefined;
|
|
16
|
+
} | {
|
|
17
|
+
name: string;
|
|
18
|
+
path: string;
|
|
19
|
+
playground?: undefined;
|
|
20
|
+
})[];
|
|
21
|
+
binds: {
|
|
22
|
+
address: string;
|
|
23
|
+
}[];
|
|
24
|
+
proxy_protocol: boolean;
|
|
25
|
+
}[];
|
|
26
|
+
trusted_proxies: string[];
|
|
27
|
+
public_base: string;
|
|
28
|
+
issuer: string;
|
|
29
|
+
};
|
|
30
|
+
database: {
|
|
31
|
+
host: string;
|
|
32
|
+
port: number;
|
|
33
|
+
database: string;
|
|
34
|
+
username: string;
|
|
35
|
+
password: string;
|
|
36
|
+
max_connections: number;
|
|
37
|
+
min_connections: number;
|
|
38
|
+
connect_timeout: number;
|
|
39
|
+
idle_timeout: number;
|
|
40
|
+
max_lifetime: number;
|
|
41
|
+
};
|
|
42
|
+
telemetry: {
|
|
43
|
+
tracing: {
|
|
44
|
+
exporter: string;
|
|
45
|
+
propagators: never[];
|
|
46
|
+
};
|
|
47
|
+
metrics: {
|
|
48
|
+
exporter: string;
|
|
49
|
+
};
|
|
50
|
+
sentry: {
|
|
51
|
+
dsn: null;
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
templates: {
|
|
55
|
+
path: string;
|
|
56
|
+
assets_manifest: string;
|
|
57
|
+
translations_path: string;
|
|
58
|
+
};
|
|
59
|
+
email: {
|
|
60
|
+
from: string;
|
|
61
|
+
reply_to: string;
|
|
62
|
+
transport: string;
|
|
63
|
+
mode: string;
|
|
64
|
+
hostname: string;
|
|
65
|
+
port: number;
|
|
66
|
+
username: string;
|
|
67
|
+
password: string;
|
|
68
|
+
};
|
|
69
|
+
secrets: {
|
|
70
|
+
encryption: string;
|
|
71
|
+
keys: {
|
|
72
|
+
kid: string;
|
|
73
|
+
key: string;
|
|
74
|
+
}[];
|
|
75
|
+
};
|
|
76
|
+
passwords: {
|
|
77
|
+
enabled: boolean;
|
|
78
|
+
schemes: {
|
|
79
|
+
version: number;
|
|
80
|
+
algorithm: string;
|
|
81
|
+
}[];
|
|
82
|
+
minimum_complexity: number;
|
|
83
|
+
};
|
|
84
|
+
policy: {
|
|
85
|
+
wasm_module: string;
|
|
86
|
+
client_registration_entrypoint: string;
|
|
87
|
+
register_entrypoint: string;
|
|
88
|
+
authorization_grant_entrypoint: string;
|
|
89
|
+
password_entrypoint: string;
|
|
90
|
+
email_entrypoint: string;
|
|
91
|
+
data: {
|
|
92
|
+
client_registration: {
|
|
93
|
+
allow_insecure_uris: boolean;
|
|
94
|
+
allow_missing_contacts: boolean;
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
upstream_oauth2: {
|
|
99
|
+
providers: never[];
|
|
100
|
+
};
|
|
101
|
+
branding: {
|
|
102
|
+
service_name: null;
|
|
103
|
+
policy_uri: null;
|
|
104
|
+
tos_uri: null;
|
|
105
|
+
imprint: null;
|
|
106
|
+
logo_uri: null;
|
|
107
|
+
};
|
|
108
|
+
account: {
|
|
109
|
+
password_registration_enabled: boolean;
|
|
110
|
+
};
|
|
111
|
+
experimental: {
|
|
112
|
+
access_token_ttl: number;
|
|
113
|
+
compat_token_ttl: number;
|
|
114
|
+
};
|
|
115
|
+
rate_limiting: {
|
|
116
|
+
login: {
|
|
117
|
+
burst: number;
|
|
118
|
+
per_second: number;
|
|
119
|
+
};
|
|
120
|
+
registration: {
|
|
121
|
+
burst: number;
|
|
122
|
+
per_second: number;
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Incomplete type for the MAS configuration.
|
|
128
|
+
*/
|
|
129
|
+
export type MasConfig = typeof DEFAULT_CONFIG;
|
|
130
|
+
/**
|
|
131
|
+
* A container running the Matrix Authentication Service.
|
|
132
|
+
*
|
|
133
|
+
* Exposes the MAS API on port 8080 and the health check on port 8081.
|
|
134
|
+
* Waits for HTTP /health on port 8081 to be available.
|
|
135
|
+
*/
|
|
136
|
+
export declare class MatrixAuthenticationServiceContainer extends GenericContainer {
|
|
137
|
+
private config;
|
|
138
|
+
private readonly args;
|
|
139
|
+
constructor(db: StartedPostgreSqlContainer);
|
|
140
|
+
/**
|
|
141
|
+
* Adds additional configuration to the MAS config.
|
|
142
|
+
* @param config - additional config fields to add
|
|
143
|
+
*/
|
|
144
|
+
withConfig(config: object): this;
|
|
145
|
+
/**
|
|
146
|
+
* Starts the MAS container
|
|
147
|
+
*/
|
|
148
|
+
start(): Promise<StartedMatrixAuthenticationServiceContainer>;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* A started Matrix Authentication Service container.
|
|
152
|
+
*/
|
|
153
|
+
export declare class StartedMatrixAuthenticationServiceContainer extends AbstractStartedContainer {
|
|
154
|
+
readonly baseUrl: string;
|
|
155
|
+
private readonly args;
|
|
156
|
+
private adminTokenPromise?;
|
|
157
|
+
constructor(container: StartedTestContainer, baseUrl: string, args: string[]);
|
|
158
|
+
/**
|
|
159
|
+
* Retrieves a valid HS admin token
|
|
160
|
+
*/
|
|
161
|
+
getAdminToken(): Promise<string>;
|
|
162
|
+
private manage;
|
|
163
|
+
private manageRegisterUser;
|
|
164
|
+
private manageIssueCompatibilityToken;
|
|
165
|
+
private registerUserInternal;
|
|
166
|
+
/**
|
|
167
|
+
* Registers a user
|
|
168
|
+
* @param username - the username of the user to register
|
|
169
|
+
* @param password - the password of the user to register
|
|
170
|
+
* @param displayName - optional display name to set on the newly registered user
|
|
171
|
+
*/
|
|
172
|
+
registerUser(username: string, password: string, displayName?: string): Promise<Credentials>;
|
|
173
|
+
/**
|
|
174
|
+
* Binds a 3pid
|
|
175
|
+
* @param username - the username of the user to bind the 3pid to
|
|
176
|
+
* @param medium - the medium of the 3pid to bind
|
|
177
|
+
* @param address - the address of the 3pid to bind
|
|
178
|
+
*/
|
|
179
|
+
setThreepid(username: string, medium: string, address: string): Promise<void>;
|
|
180
|
+
}
|
|
181
|
+
export {};
|
|
182
|
+
//# sourceMappingURL=mas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mas.d.ts","sourceRoot":"","sources":["../../src/testcontainers/mas.ts"],"names":[],"mappings":"AAOA,OAAO,EACH,wBAAwB,EACxB,gBAAgB,EAChB,KAAK,oBAAoB,EAG5B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,KAAK,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAK7E,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,QAAA,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8JnB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,cAAc,CAAC;AAE9C;;;;;GAKG;AACH,qBAAa,oCAAqC,SAAQ,gBAAgB;IACtE,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAiC;gBAEnC,EAAE,EAAE,0BAA0B;IAcjD;;;OAGG;IACI,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAQvC;;OAEG;IACmB,KAAK,IAAI,OAAO,CAAC,2CAA2C,CAAC;CAuBtF;AAED;;GAEG;AACH,qBAAa,2CAA4C,SAAQ,wBAAwB;aAKjE,OAAO,EAAE,MAAM;IAC/B,OAAO,CAAC,QAAQ,CAAC,IAAI;IALzB,OAAO,CAAC,iBAAiB,CAAC,CAAkB;gBAGxC,SAAS,EAAE,oBAAoB,EACf,OAAO,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EAAE;IAKnC;;OAEG;IACU,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;YAY/B,MAAM;YAQN,kBAAkB;YAgClB,6BAA6B;YAmB7B,oBAAoB;IAoBlC;;;;;OAKG;IACU,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAIzG;;;;;OAKG;IACU,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAO7F"}
|