Skip to content

Commit

Permalink
Merge pull request #27 from nordeck/nic/feat/add-lifecycle-logic
Browse files Browse the repository at this point in the history
Add the widget lifecycle module
  • Loading branch information
dhenneke committed Sep 25, 2023
2 parents e20c88c + 52f7ab8 commit aeacacb
Show file tree
Hide file tree
Showing 42 changed files with 1,573 additions and 112 deletions.
4 changes: 4 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ updates:
development-dependencies:
dependency-type: 'development'
ignore:
# The version of the module api describes the compatibility with the Element version. Every
# package can use a different version and we only want to update it if we need new
# features.
- dependency-name: '@matrix-org/react-sdk-module-api'
# For TypeScript, ignore all updates. Stick to the version of @microsoft/api-extractor.
- dependency-name: 'typescript'
# Stick to the react version of Element
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ jobs:
run: yarn install --frozen-lockfile

- name: build
run: yarn build
run: yarn e2e:build

- name: Install Playwright Browsers
run: npx playwright install --with-deps chromium
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ This module also requires the installation of a Synapse module:
- See the [Readme of the `element-web-guest-module`](./packages/element-web-guest-module/README.md) for instructions on how to install it in Element.
- See the [Readme of the `synapse-guest-module`](./packages/synapse-guest-module/README.md) for instructions on how to install it in your Synapse homeserver.

### Element Web Widget Lifecycle Module

A module to approve widget capabilities so the user is asked:

- See the [Readme of the `element-web-widget-lifecycle-module`](./packages/element-web-widget-lifecycle-module/README.md) for instructions on how to install it in Element.

## Getting Started

Development on the module happens at [GitHub](https://github.com/nordeck/element-web-modules).
Expand Down
2 changes: 1 addition & 1 deletion e2e/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Running the e2e tests requires Docker to be installed.
### Running Tests

The e2e tests are testing the guest module for Element and for Synapse.
Make sure to always run `yarn build` in the root folder before initially running the tests or after changing a component.
Make sure to always run `yarn e2e:build` in the root folder before initially running the tests or after changing a component.

1. **Synapse Module**: By default, it uses the image that was built by running `yarn docker:build` in the root folder of this repository.
Building the container at least once is required to run the tests.
Expand Down
7 changes: 4 additions & 3 deletions e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,19 @@
"cross-fetch": "^4.0.0",
"eslint": "^8.49.0",
"eslint-plugin-playwright": "^0.16.0",
"shx": "^0.3.4",
"testcontainers": "^10.2.1",
"typescript": "~5.0.4"
},
"scripts": {
"clean": "echo \"Nothing to clean\"",
"build": "yarn workspace @nordeck/element-web-guest-module build && yarn workspace @nordeck/element-web-guest-module package && shx rm -rf src/deploy/elementWeb/*.tgz && shx cp ../packages/element-web-guest-module/*.tgz src/deploy/elementWeb/element-web-guest-module.tgz && yarn workspace @nordeck/synapse-guest-module docker:build",
"build": "echo \"Nothing to build\"",
"docker:build": "echo \"Nothing to build\"",
"tsc": "tsc",
"lint": "eslint . --max-warnings=0",
"depcheck": "depcheck --ignores=typescript",
"e2e": "playwright test --headed --project=chromium --workers=1 --reporter=dot",
"e2e:all": "yarn playwright test --reporter=dot"
"e2e:all": "yarn playwright test --reporter=dot",
"e2e:build": "yarn workspaces run package && shx rm -rf src/deploy/elementWeb/*.tgz && shx cp ../packages/**/*.tgz src/deploy/elementWeb/",
"package": "echo \"Nothing to clean\""
}
}
2 changes: 1 addition & 1 deletion e2e/src/deploy/elementWeb/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ RUN git clone --depth 1 --branch $ELEMENT_VERSION https://github.com/vector-im/e
RUN yarn --network-timeout=200000 install

# Add all configurations
COPY /*.tgz /src
COPY /*.tgz /src/
COPY /build_config.yaml /src
COPY /customisations.json /src

Expand Down
3 changes: 2 additions & 1 deletion e2e/src/deploy/elementWeb/build_config.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Register the module that was created by `yarn build`
modules:
- 'file:element-web-guest-module.tgz'
- 'file:nordeck-element-web-guest-module.tgz'
- 'file:nordeck-element-web-widget-lifecycle-module.tgz'
11 changes: 11 additions & 0 deletions e2e/src/deploy/elementWeb/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,16 @@
"guest_user_homeserver_url": "{{HOMESERVER_URL}}",
"skip_single_sign_on": true
}
},
"net.nordeck.element_web.module.widget_lifecycle": {
"widget_permissions": {
"{{WIDGET_SERVER_URL}}/widget.html": {
"preload_approved": true,
"identity_approved": true,
"capabilities_approved": [
"org.matrix.msc2762.receive.state_event:m.room.topic"
]
}
}
}
}
9 changes: 5 additions & 4 deletions e2e/src/deploy/elementWeb/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ let container: StartedTestContainer | undefined;

export async function startElementWeb({
homeserverUrl,
widgetServerUrl,
version = 'v1.11.40',
}: {
homeserverUrl: string;
widgetServerUrl: string;
version?: string;
}): Promise<{ elementWebUrl: string }> {
console.log(`Starting element web… (version ${version})`);
Expand All @@ -32,10 +34,9 @@ export async function startElementWeb({
require.resolve('./config.json'),
'utf-8',
);
const elementWebConfig = elementWebConfigTemplate.replace(
/{{HOMESERVER_URL}}/g,
homeserverUrl,
);
const elementWebConfig = elementWebConfigTemplate
.replace(/{{HOMESERVER_URL}}/g, homeserverUrl)
.replace(/{{WIDGET_SERVER_URL}}/g, widgetServerUrl);

const elementContainer = await GenericContainer.fromDockerfile(__dirname)
.withBuildArgs({ ELEMENT_VERSION: version })
Expand Down
7 changes: 7 additions & 0 deletions e2e/src/deploy/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import { FullConfig } from '@playwright/test';
import { startElementWeb } from './elementWeb';
import { startSynapse } from './synapse';
import { startWidgetServer } from './widgets';

export default async function globalSetup(_config: FullConfig) {
const { synapseUrl, registrationSecret } = await startSynapse({
Expand All @@ -25,8 +26,14 @@ export default async function globalSetup(_config: FullConfig) {
process.env.SYNAPSE_URL = synapseUrl;
process.env.SYNAPSE_REGISTRATION_SECRET = registrationSecret;

const { widgetServerUrl } = await startWidgetServer({
homeserverUrl: synapseUrl,
});
process.env.WIDGET_SERVER_URL = widgetServerUrl;

const { elementWebUrl } = await startElementWeb({
homeserverUrl: synapseUrl,
widgetServerUrl,
});
process.env.ELEMENT_WEB_URL = elementWebUrl;
}
3 changes: 2 additions & 1 deletion e2e/src/deploy/teardown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
import { FullConfig } from '@playwright/test';
import { stopElementWeb } from './elementWeb';
import { stopSynapse } from './synapse';
import { stopWidgetServer } from './widgets';

export default async function globalTeardown(_config: FullConfig) {
await Promise.all([stopSynapse(), stopElementWeb()]);
await Promise.all([stopSynapse(), stopWidgetServer(), stopElementWeb()]);
}
61 changes: 61 additions & 0 deletions e2e/src/deploy/widgets/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2023 Nordeck IT + Consulting GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { readFile } from 'fs/promises';
import * as http from 'http';
import { AddressInfo } from 'net';

let server: http.Server;

export async function startWidgetServer({
homeserverUrl,
}: {
homeserverUrl: string;
}): Promise<{
widgetServerUrl: string;
}> {
console.log(`Starting widget server…`);

const widgetHtmlTemplate = await readFile(
require.resolve('./widget.html'),
'utf-8',
);

const widgetHtml = widgetHtmlTemplate.replace(
/{{HOMESERVER_URL}}/g,
homeserverUrl,
);

server = http.createServer((_, res) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end(widgetHtml);
});
server.listen();

const widgetServerUrl = `http://localhost:${
(server.address() as AddressInfo).port
}`;
console.log('Widget server running at', widgetServerUrl);

return { widgetServerUrl };
}

export async function stopWidgetServer() {
if (server) {
server.close();
console.log('Stopped widget server');
}
}
55 changes: 55 additions & 0 deletions e2e/src/deploy/widgets/widget.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<html lang="en">
<head>
<title>Demo Widget</title>
<script>
let sendEventCount = 0;
window.onmessage = async (ev) => {
if (ev.data.action === 'capabilities') {
// Return the capabilities for this widget
window.parent.postMessage(
Object.assign(
{
response: {
capabilities: [
'org.matrix.msc2762.receive.state_event:m.room.topic',
],
},
},
ev.data,
),
'*',
);
} else if (ev.data.action === 'notify_capabilities') {
// Ask for an openid token
window.parent.postMessage(
{
api: 'fromWidget',
widgetId: ev.data.widgetId,
requestId: 'widget-' + sendEventCount,
action: 'get_openid',
data: {},
},
'*',
);
} else if (
ev.data.action === 'get_openid' &&
ev.data.response?.state === 'allowed'
) {
// Add the userid to the heading
const { matrix_server_name, access_token } = ev.data.response;

const response = await fetch(
`{{HOMESERVER_URL}}/_matrix/federation/v1/openid/userinfo?access_token=${access_token}`,
);
const { sub } = await response.json();

const titleElement = document.getElementById('title');
titleElement.innerText = `Hello ${sub}!`;
}
};
</script>
</head>
<body>
<h1 id="title">Hello unknown!</h1>
</body>
</html>
44 changes: 44 additions & 0 deletions e2e/src/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ type Fixtures = {
alicePage: Page;
aliceElementWebPage: ElementWebPage;
bob: User;
bobPage: Page;
bobElementWebPage: ElementWebPage;
guestPage: Page;
guestElementWebPage: ElementWebPage;
runAxeAnalysis: (page: Page) => Promise<string>;
Expand Down Expand Up @@ -65,6 +67,48 @@ export const test = base.extend<Fixtures>({
await use(user);
},

bobPage: async ({ browser, contextOptions, video }, use, testInfo) => {
// TODO: For some reason we are missing the video in case we are using a
// second context https://github.com/microsoft/playwright/issues/9002
// We configure it manually instead.
const videoMode = typeof video === 'string' ? video : video.mode;
const videoOptions = shouldCaptureVideo(videoMode, testInfo)
? {
recordVideo: {
dir: testInfo.outputDir,
size: typeof video !== 'string' ? video.size : undefined,
},
}
: {};

const context = await browser.newContext({
...contextOptions,
...videoOptions,
});
const page = await context.newPage();

try {
await use(page);
} finally {
await context.close();

const video = page.video();

if (video) {
const path = testInfo.outputPath('video-bob.webm');
await video.saveAs(path);
testInfo.attach('video', { path, contentType: 'video/webm' });
}
}
},

bobElementWebPage: async ({ bobPage, bob }, use) => {
const elementWebPage = new ElementWebPage(bobPage);
await elementWebPage.login(bob.username, bob.password);

await use(elementWebPage);
},

guestPage: async ({ browser, contextOptions, video }, use, testInfo) => {
// TODO: For some reason we are missing the video in case we are using a
// second context https://github.com/microsoft/playwright/issues/9002
Expand Down
Loading

0 comments on commit aeacacb

Please sign in to comment.