Create a screenshots page that will allow us to do screenshot testing

screenshots-page2
Andy Balaam 2022-01-06 10:49:51 +00:00
parent 211f480027
commit 65efe55677
4 changed files with 507 additions and 0 deletions

106
src/vector/screenshots.html Normal file
View File

@ -0,0 +1,106 @@
<!doctype html>
<html lang="en" style="height: 100%;">
<!--
A page to render components of element-web that may be used for
screenshot testing.
See src/vector/screenshots.tsx for more information.
-->
<head>
<meta charset="utf-8">
<title>Element Screenshots</title>
<link rel="apple-touch-icon" sizes="57x57" href="<%= require('../../res/vector-icons/apple-touch-icon-57.png') %>">
<link rel="apple-touch-icon" sizes="60x60" href="<%= require('../../res/vector-icons/apple-touch-icon-60.png') %>">
<link rel="apple-touch-icon" sizes="72x72" href="<%= require('../../res/vector-icons/apple-touch-icon-72.png') %>">
<link rel="apple-touch-icon" sizes="76x76" href="<%= require('../../res/vector-icons/apple-touch-icon-76.png') %>">
<link rel="apple-touch-icon" sizes="114x114" href="<%= require('../../res/vector-icons/apple-touch-icon-114.png') %>">
<link rel="apple-touch-icon" sizes="120x120" href="<%= require('../../res/vector-icons/apple-touch-icon-120.png') %>">
<link rel="apple-touch-icon" sizes="144x144" href="<%= require('../../res/vector-icons/apple-touch-icon-144.png') %>">
<link rel="apple-touch-icon" sizes="152x152" href="<%= require('../../res/vector-icons/apple-touch-icon-152.png') %>">
<link rel="apple-touch-icon" sizes="180x180" href="<%= require('../../res/vector-icons/apple-touch-icon-180.png') %>">
<link rel="manifest" href="manifest.json">
<meta name="referrer" content="no-referrer">
<link rel="shortcut icon" href="<%= require('../../res/vector-icons/favicon.ico') %>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="apple-mobile-web-app-title" content="Element">
<meta name="application-name" content="Element">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="msapplication-TileImage" content="<%= require('../../res/vector-icons/mstile-150.png') %>">
<meta name="msapplication-config" content="<%= require('../../res/vector-icons/browserconfig.xml') %>">
<meta name="theme-color" content="#ffffff">
<meta property="og:image" content="<%= og_image_url %>" />
<meta http-equiv="Content-Security-Policy" content="
default-src 'none';
style-src 'self' 'unsafe-inline';
script-src 'self' 'unsafe-eval' https://www.recaptcha.net https://www.gstatic.com;
img-src * blob: data:;
connect-src *;
font-src 'self' data:;
media-src * blob: data:;
child-src * blob: data:;
worker-src 'self';
frame-src * blob: data:;
form-action 'self';
manifest-src 'self';
">
<% for (var i=0; i < htmlWebpackPlugin.files.css.length; i++) {
var file = htmlWebpackPlugin.files.css[i];
var match = file.match(/^bundles\/.*?\/theme-(.*)\.css$/);
if (match) {
var title = match[1].charAt(0).toUpperCase() + match[1].slice(1);
%>
<link rel="stylesheet" disabled="disabled" data-mx-theme="<%= title %>" title="<%= title %>" href="<%= file %>">
<% } else { %>
<link rel="stylesheet" href="<%= file %>">
<% }
} %>
<% for (var i=0; i < htmlWebpackPlugin.tags.headTags.length; i++) {
var tag = htmlWebpackPlugin.tags.headTags[i];
var path = tag.attributes && tag.attributes.href;
if (path.indexOf("Inter") !== -1) { %>
<link rel="preload" as="font" href="<%= path %>" crossorigin="anonymous"/>
<% }
} %>
</head>
<body style="height: 100%; margin: 0;">
<noscript>Sorry, Element requires JavaScript to be enabled.</noscript> <!-- TODO: Translate this? -->
<section id="matrixchat" style="height: 100%;" class="notranslate"></section>
<script src="<%= htmlWebpackPlugin.files.js.find(entry => entry.includes("screenshots.js")) %>"></script>
<!-- Legacy supporting Prefetch images -->
<img src="<%= require('matrix-react-sdk/res/img/warning.svg') %>" width="24" height="23" style="visibility: hidden; position: absolute; top: 0px; left: 0px;"/>
<img src="<%= require('matrix-react-sdk/res/img/e2e/warning.svg') %>" width="24" height="23" style="visibility: hidden; position: absolute; top: 0px; left: 0px;"/>
<img src="<%= require('matrix-react-sdk/res/img/feather-customised/warning-triangle.svg') %>" width="24" height="23" style="visibility: hidden; position: absolute; top: 0px; left: 0px;"/>
<img src="<%= require('matrix-react-sdk/res/img/format/bold.svg') %>" width="25" height="22" style="visibility: hidden; position: absolute; top: 0px; left: 0px;"/>
<img src="<%= require('matrix-react-sdk/res/img/format/code.svg') %>" width="25" height="22" style="visibility: hidden; position: absolute; top: 0px; left: 0px;"/>
<img src="<%= require('matrix-react-sdk/res/img/format/italics.svg') %>" width="25" height="22" style="visibility: hidden; position: absolute; top: 0px; left: 0px;"/>
<img src="<%= require('matrix-react-sdk/res/img/format/quote.svg') %>" width="25" height="22" style="visibility: hidden; position: absolute; top: 0px; left: 0px;"/>
<img src="<%= require('matrix-react-sdk/res/img/format/strikethrough.svg') %>" width="25" height="22" style="visibility: hidden; position: absolute; top: 0px; left: 0px;"/>
<audio id="messageAudio">
<source src="media/message.ogg" type="audio/ogg" />
<source src="media/message.mp3" type="audio/mpeg" />
</audio>
<audio id="ringAudio" loop>
<source src="media/ring.ogg" type="audio/ogg" />
<source src="media/ring.mp3" type="audio/mpeg" />
</audio>
<audio id="ringbackAudio" loop>
<source src="media/ringback.ogg" type="audio/ogg" />
<source src="media/ringback.mp3" type="audio/mpeg" />
</audio>
<audio id="callendAudio">
<source src="media/callend.ogg" type="audio/ogg" />
<source src="media/callend.mp3" type="audio/mpeg" />
</audio>
<audio id="busyAudio">
<source src="media/busy.ogg" type="audio/ogg" />
<source src="media/busy.mp3" type="audio/mpeg" />
</audio>
<audio id="remoteAudio"></audio>
<!-- let CSS themes pass constants to the app -->
<div id="mx_theme_accentColor"></div><div id="mx_theme_secondaryAccentColor"></div><div id="mx_theme_tertiaryAccentColor"></div>
</body>
</html>

140
src/vector/screenshots.tsx Normal file
View File

@ -0,0 +1,140 @@
/*
Copyright 2022 New Vector Ltd
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.
*/
// This file is a cut-down copy of index.ts. It goes together with
// screenshots.html to create a page allowing you to choose a component,
// Which is rendered with example data, allowing us to take screenshots
// (using code in matrix-react-sdk/tests/end-to-end-tests/screenshots.js)
// and compare for any changes against previous versions.
import './modernizr';
import * as ReactDOM from "react-dom";
import React, { ChangeEvent, ReactElement } from 'react';
import { MatrixClientPeg } from "matrix-react-sdk/src/MatrixClientPeg";
import { screenshotNotificationUserSettingsTab } from './screenshots/notification_user_settings_tab';
let widthInput: HTMLInputElement;
let heightInput: HTMLInputElement;
let classInput: HTMLInputElement;
async function settled(...promises: Array<Promise<any>>) {
for (const prom of promises) {
try {
await prom;
} catch (e) {
console.error(e);
}
}
}
async function start() {
const {
preparePlatform,
loadSkin,
loadLanguage,
loadTheme,
} = await import(
/* webpackChunkName: "init" */
/* webpackPreload: true */
"./init");
try {
preparePlatform();
const loadLanguagePromise = loadLanguage();
const loadThemePromise = loadTheme();
const loadSkinPromise = loadSkin();
await settled(loadSkinPromise, loadThemePromise, loadLanguagePromise);
await loadSkinPromise;
await loadThemePromise;
await loadLanguagePromise;
await myLoadApp();
} catch (err) {
console.error(err);
}
}
async function myLoadApp() {
// We know that MatrixClientPeg is a MatrixClientPegClass
await (MatrixClientPeg as any).createClient({});
window.matrixChat = ReactDOM.render(
<div>
<input type="hidden" id="screenshot_width" value="" />
<input type="hidden" id="screenshot_height" value="" />
<input type="hidden" id="screenshot_class" value="" />
<select id="select_screenshot" defaultValue="" onChange={selectChange}>
<option value="">-- Choose component to screenshot --</option>
{
screenshots.map((screenshot) =>
<option
value={screenshot.name}
key={screenshot.name}
>{ screenshot.name }</option>,
)
}
</select>
<div id="screenshot" />
</div>,
document.getElementById('matrixchat'),
);
widthInput = document.getElementById("screenshot_width") as HTMLInputElement;
heightInput = document.getElementById("screenshot_height") as HTMLInputElement;
classInput = document.getElementById("screenshot_class") as HTMLInputElement;
}
function selectChange(event: ChangeEvent<HTMLSelectElement>) {
const screenshot = screenshots.find((scr) => scr.name === event.target.value);
if (screenshot) {
widthInput["value"] = screenshot.width.toString();
heightInput["value"] = screenshot.height.toString();
classInput["value"] = screenshot.cssClass;
ReactDOM.render(
screenshot.fn() as ReactElement,
document.getElementById('screenshot'),
);
}
}
/**
* To add more screenshots, add a row to this table.
*
* Note: width, height and cssClass act as hints to the screenshotting code in
* matrix-react-sdk about how to render the screenshot - they do not affect the
* size or appearance if you visit the page in your browser.
*/
const screenshots = [
{
name: "NotificationUserSettingsTab_wide",
fn: screenshotNotificationUserSettingsTab,
width: 800,
height: 800,
cssClass: "mx_NotificationUserSettingsTab",
},
{
name: "NotificationUserSettingsTab_narrow",
fn: screenshotNotificationUserSettingsTab,
width: 400,
height: 800,
cssClass: "mx_NotificationUserSettingsTab",
},
];
start().catch(err => {
console.error(err);
});

View File

@ -0,0 +1,244 @@
/*
Copyright 2022 New Vector Ltd
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 { IPushRules } from "matrix-js-sdk/src/@types/PushRules";
import { MatrixClientPeg } from "matrix-react-sdk/src/MatrixClientPeg";
import { PushProcessor } from 'matrix-js-sdk/src/pushprocessor';
import React, { ReactElement } from 'react';
import * as sdk from 'matrix-react-sdk';
export function screenshotNotificationUserSettingsTab(): ReactElement {
MatrixClientPeg.get().getPushRules = async () => {
return PushProcessor.rewriteDefaultRules(pushRulesJson() as IPushRules);
};
MatrixClientPeg.get().getPushers = async () => {
return { pushers: [] };
};
MatrixClientPeg.get().getThreePids = async () => {
return { threepids: [] };
};
const NotificationUserSettingsTab= sdk.getComponent(
'views.settings.tabs.user.NotificationUserSettingsTab');
return <NotificationUserSettingsTab />;
}
function pushRulesJson() {
// This is a lightly-modified paste of the JSON returned from a GET
// to /pushrules/
/* eslint-disable */
return {
"global": {
"underride": [
{
"conditions": [
{ "kind": "event_match", "key": "type", "pattern": "m.call.invite" },
],
"actions": [
"notify",
{ "set_tweak": "sound", "value": "ring" },
{ "set_tweak": "highlight", "value": false },
],
"rule_id": ".m.rule.call",
"default": true,
"enabled": true,
},
{
"conditions": [
{ "kind": "room_member_count", "is": "2" },
{ "kind": "event_match", "key": "type", "pattern": "m.room.message" },
],
"actions": [
"notify",
{ "set_tweak": "sound", "value": "default" },
{ "set_tweak": "highlight", "value": false },
],
"rule_id": ".m.rule.room_one_to_one",
"default": true,
"enabled": true,
},
{
"conditions": [
{ "kind": "room_member_count", "is": "2" },
{ "kind": "event_match", "key": "type", "pattern": "m.room.encrypted" },
],
"actions": [
"notify",
{ "set_tweak": "sound", "value": "default" },
{ "set_tweak": "highlight", "value": false },
],
"rule_id": ".m.rule.encrypted_room_one_to_one",
"default": true,
"enabled": true,
},
{
"conditions": [
{ "kind": "event_match", "key": "type", "pattern": "m.room.message" },
],
"actions": [
"dont_notify",
],
"rule_id": ".m.rule.message",
"default": true,
"enabled": true,
},
{
"conditions": [
{ "kind": "event_match", "key": "type", "pattern": "m.room.encrypted" },
],
"actions": [
"dont_notify",
],
"rule_id": ".m.rule.encrypted",
"default": true,
"enabled": true,
},
{
"conditions": [
{ "kind": "event_match", "key": "type", "pattern": "im.vector.modular.widgets" },
{ "kind": "event_match", "key": "content.type", "pattern": "jitsi" },
{ "kind": "event_match", "key": "state_key", "pattern": "*" },
],
"actions": [
"notify",
{ "set_tweak": "highlight", "value": false },
],
"rule_id": ".im.vector.jitsi",
"default": true,
"enabled": true,
},
],
"sender": [],
"room": [],
"content": [
{
"actions": [
"notify",
{ "set_tweak": "sound", "value": "default" },
{ "set_tweak": "highlight" },
],
"pattern": "username",
"rule_id": ".m.rule.contains_user_name",
"default": true,
"enabled": true,
},
],
"override": [
{
"conditions": [],
"actions": [
"dont_notify"
],
"rule_id": ".m.rule.master",
"default": true,
"enabled": false,
},
{
"conditions": [
{ "kind": "event_match", "key": "content.msgtype", "pattern": "m.notice" }
],
"actions": [
"dont_notify",
],
"rule_id": ".m.rule.suppress_notices",
"default": true,
"enabled": true,
},
{
"conditions": [
{ "kind": "event_match", "key": "type", "pattern": "m.room.member" },
{ "kind": "event_match", "key": "content.membership", "pattern": "invite" },
{ "kind": "event_match", "key": "state_key", "pattern": "@username:example.com" }
],
"actions": [
"notify",
{ "set_tweak": "sound", "value": "default" },
{ "set_tweak": "highlight", "value": false }
],
"rule_id": ".m.rule.invite_for_me",
"default": true,
"enabled": true,
},
{
"conditions": [
{ "kind": "event_match", "key": "type", "pattern": "m.room.member" }
],
"actions": [
"dont_notify",
],
"rule_id": ".m.rule.member_event",
"default": true,
"enabled": true,
},
{
"conditions": [
{ "kind": "contains_display_name" }
],
"actions": [
"notify",
{ "set_tweak": "sound", "value": "default" },
{ "set_tweak": "highlight" }
],
"rule_id": ".m.rule.contains_display_name",
"default": true,
"enabled": true,
},
{
"conditions": [
{ "kind": "event_match", "key": "content.body", "pattern": "@room" },
{ "kind": "sender_notification_permission", "key": "room" }
],
"actions": [
"notify",
{ "set_tweak": "highlight", "value": true }
],
"rule_id": ".m.rule.roomnotif",
"default": true,
"enabled": true,
},
{
"conditions": [
{ "kind": "event_match", "key": "type", "pattern": "m.room.tombstone" },
{ "kind": "event_match", "key": "state_key", "pattern": "" }
],
"actions": [
"notify",
{ "set_tweak": "highlight", "value": true }
],
"rule_id": ".m.rule.tombstone",
"default": true,
"enabled": true,
},
{
"conditions": [
{ "kind": "event_match", "key": "type", "pattern": "m.reaction" }
],
"actions": [
"dont_notify",
],
"rule_id": ".m.rule.reaction",
"default": true,
"enabled": true,
}
],
},
"device": {},
};
/* eslint-enable */
}

View File

@ -99,6 +99,7 @@ module.exports = (env, argv) => {
entry: {
"bundle": "./src/vector/index.ts",
"screenshots": "./src/vector/screenshots.tsx",
"mobileguide": "./src/vector/mobile_guide/index.ts",
"jitsi": "./src/vector/jitsi/index.ts",
"usercontent": "./node_modules/matrix-react-sdk/src/usercontent/index.ts",
@ -496,6 +497,22 @@ module.exports = (env, argv) => {
},
}),
// This is our screenshots page for testing
new HtmlWebpackPlugin({
template: './src/vector/screenshots.html',
filename: 'screenshots.html',
// we inject the links ourselves via the template, because
// HtmlWebpackPlugin will screw up our formatting like the names
// of the themes and which chunks we actually care about.
inject: false,
excludeChunks: ['mobileguide', 'usercontent', 'jitsi'],
minify: false,
templateParameters: {
og_image_url: ogImageUrl,
},
}),
// This is the jitsi widget wrapper (embedded, so isolated stack)
new HtmlWebpackPlugin({
template: './src/vector/jitsi/index.html',