mirror of
https://github.com/element-hq/element-web
synced 2024-11-25 10:45:51 +03:00
Merge branch 'develop' into t3chguy/wat/230.1
This commit is contained in:
commit
3620c5ac62
32 changed files with 898 additions and 244 deletions
|
@ -122,8 +122,6 @@ module.exports = {
|
|||
"!matrix-js-sdk/src/crypto/aes",
|
||||
"!matrix-js-sdk/src/crypto/keybackup",
|
||||
"!matrix-js-sdk/src/crypto/deviceinfo",
|
||||
"!matrix-js-sdk/src/crypto/key_passphrase",
|
||||
"!matrix-js-sdk/src/crypto/recoverykey",
|
||||
"!matrix-js-sdk/src/crypto/dehydration",
|
||||
"!matrix-js-sdk/src/oidc",
|
||||
"!matrix-js-sdk/src/oidc/discovery",
|
||||
|
|
2
.github/workflows/notify-element-web.yml
vendored
2
.github/workflows/notify-element-web.yml
vendored
|
@ -9,7 +9,7 @@ jobs:
|
|||
name: "Notify Element Web"
|
||||
runs-on: ubuntu-latest
|
||||
# Only respect triggers from our develop branch, ignore that of forks
|
||||
if: github.repository == 'matrix-org/matrix-react-sdk'
|
||||
if: github.repository == 'element-hq/matrix-react-sdk'
|
||||
steps:
|
||||
- name: Notify element-web repo that a new SDK build is on develop
|
||||
uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # v3
|
||||
|
|
|
@ -20,7 +20,7 @@ jobs:
|
|||
|
||||
- name: Create Pull Request
|
||||
id: cpr
|
||||
uses: peter-evans/create-pull-request@4320041ed380b20e97d388d56a7fb4f9b8c20e79 # v7
|
||||
uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7
|
||||
with:
|
||||
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
branch: actions/playwright-image-updates
|
||||
|
|
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
|
@ -19,7 +19,9 @@ concurrency: ${{ github.workflow }}
|
|||
jobs:
|
||||
release:
|
||||
uses: matrix-org/matrix-js-sdk/.github/workflows/release-make.yml@develop
|
||||
secrets: inherit
|
||||
secrets:
|
||||
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
with:
|
||||
final: ${{ inputs.mode == 'final' }}
|
||||
npm: ${{ inputs.npm }}
|
||||
|
|
6
.github/workflows/tests.yml
vendored
6
.github/workflows/tests.yml
vendored
|
@ -39,7 +39,7 @@ jobs:
|
|||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ inputs.matrix-js-sdk-sha && 'matrix-org/matrix-react-sdk' || github.repository }}
|
||||
repository: ${{ inputs.matrix-js-sdk-sha && 'element-hq/matrix-react-sdk' || github.repository }}
|
||||
|
||||
- name: Yarn cache
|
||||
uses: actions/setup-node@v4
|
||||
|
@ -96,7 +96,7 @@ jobs:
|
|||
needs: jest
|
||||
steps:
|
||||
- name: Skip SonarCloud
|
||||
uses: Sibz/github-status-action@071b5370da85afbb16637d6eed8524a06bc2053e # v1
|
||||
uses: Sibz/github-status-action@faaa4d96fecf273bd762985e0e7f9f933c774918 # v1
|
||||
with:
|
||||
authToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
state: success
|
||||
|
@ -111,7 +111,7 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ inputs.matrix-js-sdk-sha && 'matrix-org/matrix-react-sdk' || github.repository }}
|
||||
repository: ${{ inputs.matrix-js-sdk-sha && 'element-hq/matrix-react-sdk' || github.repository }}
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
},
|
||||
"resolutions": {
|
||||
"@types/react-dom": "17.0.25",
|
||||
"@types/react": "17.0.80",
|
||||
"@types/react": "17.0.82",
|
||||
"@types/seedrandom": "3.0.8",
|
||||
"oidc-client-ts": "3.0.1",
|
||||
"jwt-decode": "4.0.0",
|
||||
|
@ -91,7 +91,7 @@
|
|||
"classnames": "^2.2.6",
|
||||
"commonmark": "^0.31.0",
|
||||
"counterpart": "^0.18.6",
|
||||
"css-tree": "^2.3.1",
|
||||
"css-tree": "^3.0.0",
|
||||
"diff-dom": "^5.0.0",
|
||||
"diff-match-patch": "^1.0.5",
|
||||
"emojibase-regex": "15.3.2",
|
||||
|
@ -183,7 +183,7 @@
|
|||
"@types/node-fetch": "^2.6.2",
|
||||
"@types/pako": "^2.0.0",
|
||||
"@types/qrcode": "^1.3.5",
|
||||
"@types/react": "17.0.80",
|
||||
"@types/react": "17.0.82",
|
||||
"@types/react-beautiful-dnd": "^13.0.0",
|
||||
"@types/react-dom": "17.0.25",
|
||||
"@types/react-transition-group": "^4.4.0",
|
||||
|
@ -198,7 +198,7 @@
|
|||
"axe-core": "4.10.0",
|
||||
"babel-jest": "^29.0.0",
|
||||
"blob-polyfill": "^9.0.0",
|
||||
"eslint": "8.57.0",
|
||||
"eslint": "8.57.1",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-deprecate": "0.8.5",
|
||||
|
|
|
@ -14,7 +14,7 @@ import type { Logger } from "matrix-js-sdk/src/logger";
|
|||
import type { SecretStorageKeyDescription } from "matrix-js-sdk/src/secret-storage";
|
||||
import type { Credentials, HomeserverInstance } from "../plugins/homeserver";
|
||||
import type { GeneratedSecretStorageKey } from "matrix-js-sdk/src/crypto-api";
|
||||
import { Client } from "./client";
|
||||
import { bootstrapCrossSigningForClient, Client } from "./client";
|
||||
|
||||
export interface CreateBotOpts {
|
||||
/**
|
||||
|
@ -90,9 +90,13 @@ export class Bot extends Client {
|
|||
}
|
||||
|
||||
protected async getClientHandle(): Promise<JSHandle<ExtendedMatrixClient>> {
|
||||
if (this.handlePromise) return this.handlePromise;
|
||||
if (!this.handlePromise) this.handlePromise = this.buildClient();
|
||||
return this.handlePromise;
|
||||
}
|
||||
|
||||
this.handlePromise = this.page.evaluateHandle(
|
||||
private async buildClient(): Promise<JSHandle<ExtendedMatrixClient>> {
|
||||
const credentials = await this.getCredentials();
|
||||
const clientHandle = await this.page.evaluateHandle(
|
||||
async ({ homeserver, credentials, opts }) => {
|
||||
function getLogger(loggerName: string): Logger {
|
||||
const logger = {
|
||||
|
@ -172,53 +176,50 @@ export class Bot extends Client {
|
|||
});
|
||||
}
|
||||
|
||||
if (!opts.startClient) {
|
||||
return cli;
|
||||
}
|
||||
|
||||
await cli.initRustCrypto({ useIndexedDB: false });
|
||||
cli.setGlobalErrorOnUnknownDevices(false);
|
||||
await cli.startClient();
|
||||
|
||||
if (opts.bootstrapCrossSigning) {
|
||||
// XXX: workaround https://github.com/element-hq/element-web/issues/26755
|
||||
// wait for out device list to be available, as a proxy for the device keys having been uploaded.
|
||||
await cli.getCrypto()!.getUserDeviceInfo([credentials.userId]);
|
||||
|
||||
await cli.getCrypto()!.bootstrapCrossSigning({
|
||||
authUploadDeviceSigningKeys: async (func) => {
|
||||
await func({
|
||||
type: "m.login.password",
|
||||
identifier: {
|
||||
type: "m.id.user",
|
||||
user: credentials.userId,
|
||||
},
|
||||
password: credentials.password,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (opts.bootstrapSecretStorage) {
|
||||
const passphrase = "new passphrase";
|
||||
const recoveryKey = await cli.getCrypto().createRecoveryKeyFromPassphrase(passphrase);
|
||||
Object.assign(cli, { __playwright_recovery_key: recoveryKey });
|
||||
|
||||
await cli.getCrypto()!.bootstrapSecretStorage({
|
||||
setupNewSecretStorage: true,
|
||||
setupNewKeyBackup: true,
|
||||
createSecretStorageKey: () => Promise.resolve(recoveryKey),
|
||||
});
|
||||
}
|
||||
|
||||
return cli;
|
||||
},
|
||||
{
|
||||
homeserver: this.homeserver.config,
|
||||
credentials: await this.getCredentials(),
|
||||
credentials,
|
||||
opts: this.opts,
|
||||
},
|
||||
);
|
||||
return this.handlePromise;
|
||||
|
||||
// If we weren't configured to start the client, bail out now.
|
||||
if (!this.opts.startClient) {
|
||||
return clientHandle;
|
||||
}
|
||||
|
||||
await clientHandle.evaluate(async (cli) => {
|
||||
await cli.initRustCrypto({ useIndexedDB: false });
|
||||
cli.setGlobalErrorOnUnknownDevices(false);
|
||||
await cli.startClient();
|
||||
});
|
||||
|
||||
if (this.opts.bootstrapCrossSigning) {
|
||||
// XXX: workaround https://github.com/element-hq/element-web/issues/26755
|
||||
// wait for out device list to be available, as a proxy for the device keys having been uploaded.
|
||||
await clientHandle.evaluate(async (cli, credentials) => {
|
||||
await cli.getCrypto()!.getUserDeviceInfo([credentials.userId]);
|
||||
}, credentials);
|
||||
|
||||
await bootstrapCrossSigningForClient(clientHandle, credentials);
|
||||
}
|
||||
|
||||
if (this.opts.bootstrapSecretStorage) {
|
||||
await clientHandle.evaluate(async (cli) => {
|
||||
const passphrase = "new passphrase";
|
||||
const recoveryKey = await cli.getCrypto().createRecoveryKeyFromPassphrase(passphrase);
|
||||
Object.assign(cli, { __playwright_recovery_key: recoveryKey });
|
||||
|
||||
await cli.getCrypto()!.bootstrapSecretStorage({
|
||||
setupNewSecretStorage: true,
|
||||
setupNewKeyBackup: true,
|
||||
createSecretStorageKey: () => Promise.resolve(recoveryKey),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return clientHandle;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -356,24 +356,11 @@ export class Client {
|
|||
}
|
||||
|
||||
/**
|
||||
* Boostraps cross-signing.
|
||||
* Bootstraps cross-signing.
|
||||
*/
|
||||
public async bootstrapCrossSigning(credentials: Credentials): Promise<void> {
|
||||
const client = await this.prepareClient();
|
||||
return client.evaluate(async (client, credentials) => {
|
||||
await client.getCrypto().bootstrapCrossSigning({
|
||||
authUploadDeviceSigningKeys: async (func) => {
|
||||
await func({
|
||||
type: "m.login.password",
|
||||
identifier: {
|
||||
type: "m.id.user",
|
||||
user: credentials.userId,
|
||||
},
|
||||
password: credentials.password,
|
||||
});
|
||||
},
|
||||
});
|
||||
}, credentials);
|
||||
return bootstrapCrossSigningForClient(client, credentials);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -439,3 +426,31 @@ export class Client {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** Call `CryptoApi.bootstrapCrossSigning` on the given Matrix client, using the given credentials to authenticate
|
||||
* the UIA request.
|
||||
*/
|
||||
export function bootstrapCrossSigningForClient(
|
||||
client: JSHandle<MatrixClient>,
|
||||
credentials: Credentials,
|
||||
resetKeys: boolean = false,
|
||||
) {
|
||||
return client.evaluate(
|
||||
async (client, { credentials, resetKeys }) => {
|
||||
await client.getCrypto().bootstrapCrossSigning({
|
||||
authUploadDeviceSigningKeys: async (func) => {
|
||||
await func({
|
||||
type: "m.login.password",
|
||||
identifier: {
|
||||
type: "m.id.user",
|
||||
user: credentials.userId,
|
||||
},
|
||||
password: credentials.password,
|
||||
});
|
||||
},
|
||||
setupNewCrossSigning: resetKeys,
|
||||
});
|
||||
},
|
||||
{ credentials, resetKeys },
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,8 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
|||
*/
|
||||
|
||||
import { ICryptoCallbacks, SecretStorage } from "matrix-js-sdk/src/matrix";
|
||||
import { deriveKey } from "matrix-js-sdk/src/crypto/key_passphrase";
|
||||
import { decodeRecoveryKey } from "matrix-js-sdk/src/crypto/recoverykey";
|
||||
import { deriveRecoveryKeyFromPassphrase, decodeRecoveryKey } from "matrix-js-sdk/src/crypto-api";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import type CreateSecretStorageDialog from "./async-components/views/dialogs/security/CreateSecretStorageDialog";
|
||||
|
@ -64,7 +63,7 @@ function makeInputToKey(
|
|||
): (keyParams: KeyParams) => Promise<Uint8Array> {
|
||||
return async ({ passphrase, recoveryKey }): Promise<Uint8Array> => {
|
||||
if (passphrase) {
|
||||
return deriveKey(passphrase, keyInfo.passphrase.salt, keyInfo.passphrase.iterations);
|
||||
return deriveRecoveryKeyFromPassphrase(passphrase, keyInfo.passphrase.salt, keyInfo.passphrase.iterations);
|
||||
} else if (recoveryKey) {
|
||||
return decodeRecoveryKey(recoveryKey);
|
||||
}
|
||||
|
|
|
@ -952,18 +952,20 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
}
|
||||
|
||||
private async startRegistration(params: { [key: string]: string }, isMobileRegistration?: boolean): Promise<void> {
|
||||
if (!SettingsStore.getValue(UIFeature.Registration)) {
|
||||
// If registration is disabled or mobile registration is requested but not enabled in settings redirect to the welcome screen
|
||||
if (
|
||||
!SettingsStore.getValue(UIFeature.Registration) ||
|
||||
(isMobileRegistration && !SettingsStore.getValue("Registration.mobileRegistrationHelper"))
|
||||
) {
|
||||
this.showScreen("welcome");
|
||||
return;
|
||||
}
|
||||
const isMobileRegistrationAllowed =
|
||||
isMobileRegistration && SettingsStore.getValue("Registration.mobileRegistrationHelper");
|
||||
|
||||
const newState: Partial<IState> = {
|
||||
view: Views.REGISTER,
|
||||
};
|
||||
|
||||
if (isMobileRegistrationAllowed && params.hs_url) {
|
||||
if (isMobileRegistration && params.hs_url) {
|
||||
try {
|
||||
const config = await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(params.hs_url);
|
||||
newState.serverConfig = config;
|
||||
|
@ -992,12 +994,12 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
newState.register_id_sid = params.sid;
|
||||
}
|
||||
|
||||
newState.isMobileRegistration = isMobileRegistrationAllowed;
|
||||
newState.isMobileRegistration = isMobileRegistration;
|
||||
|
||||
this.setStateForNewView(newState);
|
||||
ThemeController.isLogin = true;
|
||||
this.themeWatcher.recheck();
|
||||
this.notifyNewScreen(isMobileRegistrationAllowed ? "mobile_register" : "register");
|
||||
this.notifyNewScreen(isMobileRegistration ? "mobile_register" : "register");
|
||||
}
|
||||
|
||||
// switch view to the given room
|
||||
|
|
|
@ -1364,7 +1364,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
|
|||
if (containsEmoji(ev.getContent(), effect.emojis) || ev.getContent().msgtype === effect.msgType) {
|
||||
// For initial threads launch, chat effects are disabled see #19731
|
||||
if (!ev.isRelation(THREAD_RELATION_TYPE.name)) {
|
||||
dis.dispatch({ action: `effects.${effect.command}` });
|
||||
dis.dispatch({ action: `effects.${effect.command}`, event: ev });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -627,6 +627,7 @@ export default class Registration extends React.Component<IProps, IState> {
|
|||
serverConfig={this.props.serverConfig}
|
||||
canSubmit={!this.state.serverErrorIsFatal}
|
||||
matrixClient={this.state.matrixClient}
|
||||
mobileRegister={this.props.mobileRegister}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
@ -779,7 +780,11 @@ export default class Registration extends React.Component<IProps, IState> {
|
|||
);
|
||||
}
|
||||
if (this.props.mobileRegister) {
|
||||
return <div className="mx_MobileRegister_body">{body}</div>;
|
||||
return (
|
||||
<div className="mx_MobileRegister_body" data-testid="mobile-register">
|
||||
{body}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<AuthPage>
|
||||
|
|
|
@ -63,6 +63,19 @@ export default class CaptchaForm extends React.Component<ICaptchaFormProps, ICap
|
|||
|
||||
public componentWillUnmount(): void {
|
||||
this.resetRecaptcha();
|
||||
// Resettting the captcha does not clear the challenge overlay from the body in android webviews.
|
||||
// Search for an iframe with the challenge src and remove it's topmost ancestor from the body.
|
||||
// TODO: Remove this when the "mobile_register" page is retired.
|
||||
const iframes = document.querySelectorAll("iframe");
|
||||
for (const iframe of iframes) {
|
||||
if (iframe.src.includes("https://www.recaptcha.net/recaptcha/api2/bframe")) {
|
||||
let parentBeforeBody: HTMLElement | null = iframe;
|
||||
do {
|
||||
parentBeforeBody = parentBeforeBody.parentElement;
|
||||
} while (parentBeforeBody?.parentElement && parentBeforeBody.parentElement != document.body);
|
||||
parentBeforeBody?.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Borrowed directly from: https://github.com/codeep/react-recaptcha-google/commit/e118fa5670fa268426969323b2e7fe77698376ba
|
||||
|
|
|
@ -12,6 +12,7 @@ import Field, { IInputProps } from "../elements/Field";
|
|||
import { _t, _td, TranslationKey } from "../../../languageHandler";
|
||||
import withValidation, { IFieldState, IValidationResult } from "../elements/Validation";
|
||||
import * as Email from "../../../email";
|
||||
import { Alignment } from "../elements/Tooltip";
|
||||
|
||||
interface IProps extends Omit<IInputProps, "onValidate" | "element"> {
|
||||
id?: string;
|
||||
|
@ -22,6 +23,7 @@ interface IProps extends Omit<IInputProps, "onValidate" | "element"> {
|
|||
label: TranslationKey;
|
||||
labelRequired: TranslationKey;
|
||||
labelInvalid: TranslationKey;
|
||||
tooltipAlignment?: Alignment;
|
||||
|
||||
// When present, completely overrides the default validation rules.
|
||||
validationRules?: (fieldState: IFieldState) => Promise<IValidationResult>;
|
||||
|
@ -77,6 +79,7 @@ class EmailField extends PureComponent<IProps> {
|
|||
autoFocus={this.props.autoFocus}
|
||||
onChange={this.props.onChange}
|
||||
onValidate={this.onValidate}
|
||||
tooltipAlignment={this.props.tooltipAlignment}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import React, { PureComponent, RefCallback, RefObject } from "react";
|
|||
import Field, { IInputProps } from "../elements/Field";
|
||||
import withValidation, { IFieldState, IValidationResult } from "../elements/Validation";
|
||||
import { _t, _td, TranslationKey } from "../../../languageHandler";
|
||||
import { Alignment } from "../elements/Tooltip";
|
||||
|
||||
interface IProps extends Omit<IInputProps, "onValidate" | "label" | "element"> {
|
||||
id?: string;
|
||||
|
@ -22,7 +23,7 @@ interface IProps extends Omit<IInputProps, "onValidate" | "label" | "element"> {
|
|||
label: TranslationKey;
|
||||
labelRequired: TranslationKey;
|
||||
labelInvalid: TranslationKey;
|
||||
|
||||
tooltipAlignment?: Alignment;
|
||||
onChange(ev: React.FormEvent<HTMLElement>): void;
|
||||
onValidate?(result: IValidationResult): void;
|
||||
}
|
||||
|
@ -70,6 +71,7 @@ class PassphraseConfirmField extends PureComponent<IProps> {
|
|||
onChange={this.props.onChange}
|
||||
onValidate={this.onValidate}
|
||||
autoFocus={this.props.autoFocus}
|
||||
tooltipAlignment={this.props.tooltipAlignment}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import withValidation, { IFieldState, IValidationResult } from "../elements/Vali
|
|||
import { _t, _td, TranslationKey } from "../../../languageHandler";
|
||||
import Field, { IInputProps } from "../elements/Field";
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import { Alignment } from "../elements/Tooltip";
|
||||
|
||||
interface IProps extends Omit<IInputProps, "onValidate" | "element"> {
|
||||
autoFocus?: boolean;
|
||||
|
@ -30,6 +31,7 @@ interface IProps extends Omit<IInputProps, "onValidate" | "element"> {
|
|||
labelEnterPassword: TranslationKey;
|
||||
labelStrongPassword: TranslationKey;
|
||||
labelAllowedButUnsafe: TranslationKey;
|
||||
tooltipAlignment?: Alignment;
|
||||
|
||||
onChange(ev: React.FormEvent<HTMLElement>): void;
|
||||
onValidate?(result: IValidationResult): void;
|
||||
|
@ -111,6 +113,7 @@ class PassphraseField extends PureComponent<IProps> {
|
|||
value={this.props.value}
|
||||
onChange={this.props.onChange}
|
||||
onValidate={this.onValidate}
|
||||
tooltipAlignment={this.props.tooltipAlignment}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import RegistrationEmailPromptDialog from "../dialogs/RegistrationEmailPromptDia
|
|||
import CountryDropdown from "./CountryDropdown";
|
||||
import PassphraseConfirmField from "./PassphraseConfirmField";
|
||||
import { PosthogAnalytics } from "../../../PosthogAnalytics";
|
||||
import { Alignment } from "../elements/Tooltip";
|
||||
|
||||
enum RegistrationField {
|
||||
Email = "field_email",
|
||||
|
@ -58,6 +59,7 @@ interface IProps {
|
|||
serverConfig: ValidatedServerConfig;
|
||||
canSubmit?: boolean;
|
||||
matrixClient: MatrixClient;
|
||||
mobileRegister?: boolean;
|
||||
|
||||
onRegisterClick(params: {
|
||||
username: string;
|
||||
|
@ -439,6 +441,13 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
|||
return true;
|
||||
}
|
||||
|
||||
private tooltipAlignment(): Alignment | undefined {
|
||||
if (this.props.mobileRegister) {
|
||||
return Alignment.Bottom;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private renderEmail(): ReactNode {
|
||||
if (!this.showEmail()) {
|
||||
return null;
|
||||
|
@ -454,6 +463,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
|||
validationRules={this.validateEmailRules.bind(this)}
|
||||
onChange={this.onEmailChange}
|
||||
onValidate={this.onEmailValidate}
|
||||
tooltipAlignment={this.tooltipAlignment()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -468,6 +478,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
|||
onChange={this.onPasswordChange}
|
||||
onValidate={this.onPasswordValidate}
|
||||
userInputs={[this.state.username]}
|
||||
tooltipAlignment={this.tooltipAlignment()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -482,6 +493,7 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
|||
password={this.state.password}
|
||||
onChange={this.onPasswordConfirmChange}
|
||||
onValidate={this.onPasswordConfirmValidate}
|
||||
tooltipAlignment={this.tooltipAlignment()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -526,6 +538,9 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
|||
value={this.state.username}
|
||||
onChange={this.onUsernameChange}
|
||||
onValidate={this.onUsernameValidate}
|
||||
tooltipAlignment={this.tooltipAlignment()}
|
||||
autoCorrect="off"
|
||||
autoCapitalize="none"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -557,14 +572,28 @@ export default class RegistrationForm extends React.PureComponent<IProps, IState
|
|||
}
|
||||
}
|
||||
|
||||
let passwordFields: JSX.Element | undefined;
|
||||
if (this.props.mobileRegister) {
|
||||
passwordFields = (
|
||||
<>
|
||||
<div className="mx_AuthBody_fieldRow">{this.renderPassword()}</div>
|
||||
<div className="mx_AuthBody_fieldRow">{this.renderPasswordConfirm()}</div>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
passwordFields = (
|
||||
<div className="mx_AuthBody_fieldRow">
|
||||
{this.renderPassword()}
|
||||
{this.renderPasswordConfirm()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={this.onSubmit}>
|
||||
<div className="mx_AuthBody_fieldRow">{this.renderUsername()}</div>
|
||||
<div className="mx_AuthBody_fieldRow">
|
||||
{this.renderPassword()}
|
||||
{this.renderPasswordConfirm()}
|
||||
</div>
|
||||
{passwordFields}
|
||||
<div className="mx_AuthBody_fieldRow">
|
||||
{this.renderEmail()}
|
||||
{this.renderPhoneNumber()}
|
||||
|
|
|
@ -10,6 +10,7 @@ import { debounce } from "lodash";
|
|||
import classNames from "classnames";
|
||||
import React, { ChangeEvent, FormEvent } from "react";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { decodeRecoveryKey } from "matrix-js-sdk/src/crypto-api";
|
||||
import { SecretStorage } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { MatrixClientPeg } from "../../../../MatrixClientPeg";
|
||||
|
@ -100,7 +101,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
|
|||
|
||||
try {
|
||||
const cli = MatrixClientPeg.safeGet();
|
||||
const decodedKey = cli.keyBackupKeyFromRecoveryKey(this.state.recoveryKey);
|
||||
const decodedKey = decodeRecoveryKey(this.state.recoveryKey);
|
||||
const correct = await cli.checkSecretStorageKey(decodedKey, this.props.keyInfo);
|
||||
this.setState({
|
||||
recoveryKeyValid: true,
|
||||
|
|
|
@ -9,9 +9,9 @@ Please see LICENSE files in the repository root for full details.
|
|||
|
||||
import React, { ChangeEvent } from "react";
|
||||
import { MatrixClient, MatrixError, SecretStorage } from "matrix-js-sdk/src/matrix";
|
||||
import { decodeRecoveryKey, KeyBackupInfo } from "matrix-js-sdk/src/crypto-api";
|
||||
import { IKeyBackupRestoreResult } from "matrix-js-sdk/src/crypto/keybackup";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { KeyBackupInfo } from "matrix-js-sdk/src/crypto-api";
|
||||
|
||||
import { MatrixClientPeg } from "../../../../MatrixClientPeg";
|
||||
import { _t } from "../../../../languageHandler";
|
||||
|
@ -118,10 +118,24 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
|
|||
accessSecretStorage(async (): Promise<void> => {}, /* forceReset = */ true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the recovery key is valid
|
||||
* @param recoveryKey
|
||||
* @private
|
||||
*/
|
||||
private isValidRecoveryKey(recoveryKey: string): boolean {
|
||||
try {
|
||||
decodeRecoveryKey(recoveryKey);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private onRecoveryKeyChange = (e: ChangeEvent<HTMLInputElement>): void => {
|
||||
this.setState({
|
||||
recoveryKey: e.target.value,
|
||||
recoveryKeyValid: MatrixClientPeg.safeGet().isValidRecoveryKey(e.target.value),
|
||||
recoveryKeyValid: this.isValidRecoveryKey(e.target.value),
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -184,7 +198,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
|
|||
{ progressCallback: this.progressCallback },
|
||||
);
|
||||
if (this.props.keyCallback) {
|
||||
const key = MatrixClientPeg.safeGet().keyBackupKeyFromRecoveryKey(this.state.recoveryKey);
|
||||
const key = decodeRecoveryKey(this.state.recoveryKey);
|
||||
this.props.keyCallback(key);
|
||||
}
|
||||
if (!this.props.showSummary) {
|
||||
|
|
|
@ -8,6 +8,7 @@ Please see LICENSE files in the repository root for full details.
|
|||
*/
|
||||
import React, { FunctionComponent, useEffect, useRef } from "react";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import dis from "../../../dispatcher/dispatcher";
|
||||
import ICanvasEffect from "../../../effects/ICanvasEffect";
|
||||
|
@ -44,9 +45,10 @@ const EffectsOverlay: FunctionComponent<IProps> = ({ roomWidth }) => {
|
|||
canvasRef.current.height = UIStore.instance.windowHeight;
|
||||
}
|
||||
};
|
||||
const onAction = (payload: { action: string }): void => {
|
||||
const onAction = (payload: { action: string; event?: MatrixEvent }): void => {
|
||||
const actionPrefix = "effects.";
|
||||
if (canvasRef.current && payload.action.startsWith(actionPrefix)) {
|
||||
const isOutdated = isEventOutdated(payload.event);
|
||||
if (canvasRef.current && payload.action.startsWith(actionPrefix) && !isOutdated) {
|
||||
const effect = payload.action.slice(actionPrefix.length);
|
||||
lazyLoadEffectModule(effect).then((module) => module?.start(canvasRef.current!));
|
||||
}
|
||||
|
@ -88,3 +90,19 @@ const EffectsOverlay: FunctionComponent<IProps> = ({ roomWidth }) => {
|
|||
};
|
||||
|
||||
export default EffectsOverlay;
|
||||
|
||||
// 48 hours
|
||||
// 48h * 60m * 60s * 1000ms
|
||||
const OUTDATED_EVENT_THRESHOLD = 48 * 60 * 60 * 1000;
|
||||
|
||||
/**
|
||||
* Return true if the event is older than 48h.
|
||||
* @param event
|
||||
*/
|
||||
function isEventOutdated(event?: MatrixEvent): boolean {
|
||||
if (!event) return false;
|
||||
|
||||
const nowTs = Date.now();
|
||||
const eventTs = event.getTs();
|
||||
return nowTs - eventTs > OUTDATED_EVENT_THRESHOLD;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import classNames from "classnames";
|
|||
import { debounce } from "lodash";
|
||||
|
||||
import { IFieldState, IValidationResult } from "./Validation";
|
||||
import Tooltip from "./Tooltip";
|
||||
import Tooltip, { Alignment } from "./Tooltip";
|
||||
import { Key } from "../../../Keyboard";
|
||||
|
||||
// Invoke validation from user input (when typing, etc.) at most once every N ms.
|
||||
|
@ -60,6 +60,8 @@ interface IProps {
|
|||
tooltipContent?: React.ReactNode;
|
||||
// If specified the tooltip will be shown regardless of feedback
|
||||
forceTooltipVisible?: boolean;
|
||||
// If specified, the tooltip with be aligned accorindly with the field, defaults to Right.
|
||||
tooltipAlignment?: Alignment;
|
||||
// If specified alongside tooltipContent, the class name to apply to the
|
||||
// tooltip itself.
|
||||
tooltipClassName?: string;
|
||||
|
@ -261,6 +263,7 @@ export default class Field extends React.PureComponent<PropShapes, IState> {
|
|||
validateOnFocus,
|
||||
usePlaceholderAsHint,
|
||||
forceTooltipVisible,
|
||||
tooltipAlignment,
|
||||
...inputProps
|
||||
} = this.props;
|
||||
|
||||
|
@ -286,7 +289,7 @@ export default class Field extends React.PureComponent<PropShapes, IState> {
|
|||
tooltipClassName={classNames("mx_Field_tooltip", "mx_Tooltip_noMargin", tooltipClassName)}
|
||||
visible={visible}
|
||||
label={tooltipContent || this.state.feedback}
|
||||
alignment={Tooltip.Alignment.Right}
|
||||
alignment={tooltipAlignment || Alignment.Right}
|
||||
role={role}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -175,7 +175,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
|||
WidgetEventCapability.forStateEvent(EventDirection.Receive, EventType.RoomCreate).raw,
|
||||
);
|
||||
|
||||
const sendRecvRoomEvents = ["io.element.call.encryption_keys"];
|
||||
const sendRecvRoomEvents = ["io.element.call.encryption_keys", EventType.Reaction, EventType.RoomRedaction];
|
||||
for (const eventType of sendRecvRoomEvents) {
|
||||
this.allowedCapabilities.add(WidgetEventCapability.forRoomEvent(EventDirection.Send, eventType).raw);
|
||||
this.allowedCapabilities.add(WidgetEventCapability.forRoomEvent(EventDirection.Receive, eventType).raw);
|
||||
|
|
|
@ -55,6 +55,7 @@ import { MatrixClientPeg as peg } from "../../../src/MatrixClientPeg";
|
|||
import DMRoomMap from "../../../src/utils/DMRoomMap";
|
||||
import { ReleaseAnnouncementStore } from "../../../src/stores/ReleaseAnnouncementStore";
|
||||
import { DRAFT_LAST_CLEANUP_KEY } from "../../../src/DraftCleaner";
|
||||
import { UIFeature } from "../../../src/settings/UIFeature";
|
||||
|
||||
jest.mock("matrix-js-sdk/src/oidc/authorize", () => ({
|
||||
completeAuthorizationCodeGrant: jest.fn(),
|
||||
|
@ -1462,4 +1463,42 @@ describe("<MatrixChat />", () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("mobile registration", () => {
|
||||
const getComponentAndWaitForReady = async (): Promise<RenderResult> => {
|
||||
const renderResult = getComponent();
|
||||
// wait for welcome page chrome render
|
||||
await screen.findByText("powered by Matrix");
|
||||
|
||||
// go to mobile_register page
|
||||
defaultDispatcher.dispatch({
|
||||
action: "start_mobile_registration",
|
||||
});
|
||||
|
||||
await flushPromises();
|
||||
|
||||
return renderResult;
|
||||
};
|
||||
|
||||
const enabledMobileRegistration = (): void => {
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation((settingName: string) => {
|
||||
if (settingName === "Registration.mobileRegistrationHelper") return true;
|
||||
if (settingName === UIFeature.Registration) return true;
|
||||
});
|
||||
};
|
||||
|
||||
it("should render welcome screen if mobile registration is not enabled in settings", async () => {
|
||||
await getComponentAndWaitForReady();
|
||||
|
||||
await screen.findByText("powered by Matrix");
|
||||
});
|
||||
|
||||
it("should render mobile registration", async () => {
|
||||
enabledMobileRegistration();
|
||||
|
||||
await getComponentAndWaitForReady();
|
||||
|
||||
expect(screen.getByTestId("mobile-register")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
|
|||
*/
|
||||
|
||||
import React from "react";
|
||||
import { fireEvent, render, screen, waitForElementToBeRemoved } from "@testing-library/react";
|
||||
import { fireEvent, render, screen, waitFor, waitForElementToBeRemoved } from "@testing-library/react";
|
||||
import { createClient, MatrixClient, MatrixError, OidcClientConfig } from "matrix-js-sdk/src/matrix";
|
||||
import { mocked, MockedObject } from "jest-mock";
|
||||
import fetchMock from "fetch-mock-jest";
|
||||
|
@ -87,12 +87,23 @@ describe("Registration", function () {
|
|||
const defaultHsUrl = "https://matrix.org";
|
||||
const defaultIsUrl = "https://vector.im";
|
||||
|
||||
function getRawComponent(hsUrl = defaultHsUrl, isUrl = defaultIsUrl, authConfig?: OidcClientConfig) {
|
||||
return <Registration {...defaultProps} serverConfig={mkServerConfig(hsUrl, isUrl, authConfig)} />;
|
||||
function getRawComponent(
|
||||
hsUrl = defaultHsUrl,
|
||||
isUrl = defaultIsUrl,
|
||||
authConfig?: OidcClientConfig,
|
||||
mobileRegister?: boolean,
|
||||
) {
|
||||
return (
|
||||
<Registration
|
||||
{...defaultProps}
|
||||
serverConfig={mkServerConfig(hsUrl, isUrl, authConfig)}
|
||||
mobileRegister={mobileRegister}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function getComponent(hsUrl?: string, isUrl?: string, authConfig?: OidcClientConfig) {
|
||||
return render(getRawComponent(hsUrl, isUrl, authConfig));
|
||||
function getComponent(hsUrl?: string, isUrl?: string, authConfig?: OidcClientConfig, mobileRegister?: boolean) {
|
||||
return render(getRawComponent(hsUrl, isUrl, authConfig, mobileRegister));
|
||||
}
|
||||
|
||||
it("should show server picker", async function () {
|
||||
|
@ -208,5 +219,31 @@ describe("Registration", function () {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when is mobile registeration", () => {
|
||||
it("should not show server picker", async function () {
|
||||
const { container } = getComponent(defaultHsUrl, defaultIsUrl, undefined, true);
|
||||
expect(container.querySelector(".mx_ServerPicker")).toBeFalsy();
|
||||
});
|
||||
|
||||
it("should show username field with autocaps disabled", async function () {
|
||||
const { container } = getComponent(defaultHsUrl, defaultIsUrl, undefined, true);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(container.querySelector("#mx_RegistrationForm_username")).toHaveAttribute(
|
||||
"autocapitalize",
|
||||
"none",
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it("should show password and confirm password fields in separate rows", async function () {
|
||||
const { container } = getComponent(defaultHsUrl, defaultIsUrl, undefined, true);
|
||||
|
||||
await waitFor(() => expect(container.querySelector("#mx_RegistrationForm_username")).toBeTruthy());
|
||||
// when password and confirm password fields are in separate rows there should be 4 rather than 3
|
||||
expect(container.querySelectorAll(".mx_AuthBody_fieldRow")).toHaveLength(4);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -58,15 +58,12 @@ describe("AccessSecretStorageDialog", () => {
|
|||
|
||||
beforeEach(() => {
|
||||
mockClient = getMockClientWithEventEmitter({
|
||||
keyBackupKeyFromRecoveryKey: jest.fn(),
|
||||
checkSecretStorageKey: jest.fn(),
|
||||
isValidRecoveryKey: jest.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
it("Closes the dialog when the form is submitted with a valid key", async () => {
|
||||
mockClient.checkSecretStorageKey.mockResolvedValue(true);
|
||||
mockClient.isValidRecoveryKey.mockReturnValue(true);
|
||||
|
||||
const onFinished = jest.fn();
|
||||
const checkPrivateKey = jest.fn().mockResolvedValue(true);
|
||||
|
@ -88,8 +85,8 @@ describe("AccessSecretStorageDialog", () => {
|
|||
const checkPrivateKey = jest.fn().mockResolvedValue(true);
|
||||
renderComponent({ onFinished, checkPrivateKey });
|
||||
|
||||
mockClient.keyBackupKeyFromRecoveryKey.mockImplementation(() => {
|
||||
throw new Error("that's no key");
|
||||
mockClient.checkSecretStorageKey.mockImplementation(() => {
|
||||
throw new Error("invalid key");
|
||||
});
|
||||
|
||||
await enterSecurityKey();
|
||||
|
@ -115,7 +112,6 @@ describe("AccessSecretStorageDialog", () => {
|
|||
};
|
||||
const checkPrivateKey = jest.fn().mockResolvedValue(false);
|
||||
renderComponent({ checkPrivateKey, keyInfo });
|
||||
mockClient.isValidRecoveryKey.mockReturnValue(false);
|
||||
|
||||
await enterSecurityKey("Security Phrase");
|
||||
expect(screen.getByPlaceholderText("Security Phrase")).toHaveValue(securityKey);
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { screen, render, waitFor } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
// Needed to be able to mock decodeRecoveryKey
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import * as recoveryKeyModule from "matrix-js-sdk/src/crypto-api/recovery-key";
|
||||
|
||||
import RestoreKeyBackupDialog from "../../../../../src/components/views/dialogs/security/RestoreKeyBackupDialog.tsx";
|
||||
import { stubClient } from "../../../../test-utils";
|
||||
|
||||
describe("<RestoreKeyBackupDialog />", () => {
|
||||
beforeEach(() => {
|
||||
stubClient();
|
||||
jest.spyOn(recoveryKeyModule, "decodeRecoveryKey").mockReturnValue(new Uint8Array(32));
|
||||
});
|
||||
|
||||
it("should render", async () => {
|
||||
const { asFragment } = render(<RestoreKeyBackupDialog onFinished={jest.fn()} />);
|
||||
await waitFor(() => expect(screen.getByText("Enter Security Key")).toBeInTheDocument());
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should display an error when recovery key is invalid", async () => {
|
||||
jest.spyOn(recoveryKeyModule, "decodeRecoveryKey").mockImplementation(() => {
|
||||
throw new Error("Invalid recovery key");
|
||||
});
|
||||
const { asFragment } = render(<RestoreKeyBackupDialog onFinished={jest.fn()} />);
|
||||
await waitFor(() => expect(screen.getByText("Enter Security Key")).toBeInTheDocument());
|
||||
|
||||
await userEvent.type(screen.getByRole("textbox"), "invalid key");
|
||||
await waitFor(() => expect(screen.getByText("👎 Not a valid Security Key")).toBeInTheDocument());
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should not raise an error when recovery is valid", async () => {
|
||||
const { asFragment } = render(<RestoreKeyBackupDialog onFinished={jest.fn()} />);
|
||||
await waitFor(() => expect(screen.getByText("Enter Security Key")).toBeInTheDocument());
|
||||
|
||||
await userEvent.type(screen.getByRole("textbox"), "valid key");
|
||||
await waitFor(() => expect(screen.getByText("👍 This looks like a valid Security Key!")).toBeInTheDocument());
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,298 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<RestoreKeyBackupDialog /> should display an error when recovery key is invalid 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_RestoreKeyBackupDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Enter Security Key
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_RestoreKeyBackupDialog_content"
|
||||
>
|
||||
<div>
|
||||
<p>
|
||||
<span>
|
||||
<b>
|
||||
Warning
|
||||
</b>
|
||||
: you should only set up key backup from a trusted computer.
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
Access your secure message history and set up secure messaging by entering your Security Key.
|
||||
</p>
|
||||
<div
|
||||
class="mx_RestoreKeyBackupDialog_primaryContainer"
|
||||
>
|
||||
<input
|
||||
class="mx_RestoreKeyBackupDialog_recoveryKeyInput"
|
||||
value="invalid key"
|
||||
/>
|
||||
<div
|
||||
class="mx_RestoreKeyBackupDialog_keyStatus"
|
||||
>
|
||||
👎 Not a valid Security Key
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
data-testid="dialog-cancel-button"
|
||||
type="button"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
disabled=""
|
||||
type="button"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span>
|
||||
If you've forgotten your Security Key you can
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link_inline"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
set up new recovery options
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<RestoreKeyBackupDialog /> should not raise an error when recovery is valid 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_RestoreKeyBackupDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Enter Security Key
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_RestoreKeyBackupDialog_content"
|
||||
>
|
||||
<div>
|
||||
<p>
|
||||
<span>
|
||||
<b>
|
||||
Warning
|
||||
</b>
|
||||
: you should only set up key backup from a trusted computer.
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
Access your secure message history and set up secure messaging by entering your Security Key.
|
||||
</p>
|
||||
<div
|
||||
class="mx_RestoreKeyBackupDialog_primaryContainer"
|
||||
>
|
||||
<input
|
||||
class="mx_RestoreKeyBackupDialog_recoveryKeyInput"
|
||||
value="valid key"
|
||||
/>
|
||||
<div
|
||||
class="mx_RestoreKeyBackupDialog_keyStatus"
|
||||
>
|
||||
👍 This looks like a valid Security Key!
|
||||
</div>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
data-testid="dialog-cancel-button"
|
||||
type="button"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
type="button"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span>
|
||||
If you've forgotten your Security Key you can
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link_inline"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
set up new recovery options
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`<RestoreKeyBackupDialog /> should render 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_RestoreKeyBackupDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Enter Security Key
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_RestoreKeyBackupDialog_content"
|
||||
>
|
||||
<div>
|
||||
<p>
|
||||
<span>
|
||||
<b>
|
||||
Warning
|
||||
</b>
|
||||
: you should only set up key backup from a trusted computer.
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
Access your secure message history and set up secure messaging by entering your Security Key.
|
||||
</p>
|
||||
<div
|
||||
class="mx_RestoreKeyBackupDialog_primaryContainer"
|
||||
>
|
||||
<input
|
||||
class="mx_RestoreKeyBackupDialog_recoveryKeyInput"
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
class="mx_RestoreKeyBackupDialog_keyStatus"
|
||||
/>
|
||||
<div
|
||||
class="mx_Dialog_buttons"
|
||||
>
|
||||
<span
|
||||
class="mx_Dialog_buttons_row"
|
||||
>
|
||||
<button
|
||||
data-testid="dialog-cancel-button"
|
||||
type="button"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
class="mx_Dialog_primary"
|
||||
data-testid="dialog-primary-button"
|
||||
disabled=""
|
||||
type="button"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span>
|
||||
If you've forgotten your Security Key you can
|
||||
<div
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_link_inline"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
set up new recovery options
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
51
test/components/views/elements/EffectsOverlay-test.tsx
Normal file
51
test/components/views/elements/EffectsOverlay-test.tsx
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2024 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { render, waitFor } from "@testing-library/react";
|
||||
|
||||
import dis from "../../../../src/dispatcher/dispatcher";
|
||||
import EffectsOverlay from "../../../../src/components/views/elements/EffectsOverlay.tsx";
|
||||
|
||||
describe("<EffectsOverlay/>", () => {
|
||||
let isStarted: boolean;
|
||||
beforeEach(() => {
|
||||
isStarted = false;
|
||||
jest.mock("../../../../src/effects/confetti/index.ts", () => {
|
||||
return class Confetti {
|
||||
start = () => {
|
||||
isStarted = true;
|
||||
};
|
||||
stop = jest.fn();
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => jest.useRealTimers());
|
||||
|
||||
it("should render", () => {
|
||||
const { asFragment } = render(<EffectsOverlay roomWidth={100} />);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should start the confetti effect", async () => {
|
||||
render(<EffectsOverlay roomWidth={100} />);
|
||||
dis.dispatch({ action: "effects.confetti" });
|
||||
await waitFor(() => expect(isStarted).toBe(true));
|
||||
});
|
||||
|
||||
it("should start the confetti effect when the event is not outdated", async () => {
|
||||
const eventDate = new Date("2024-09-01");
|
||||
const date = new Date("2024-09-02");
|
||||
jest.useFakeTimers().setSystemTime(date);
|
||||
|
||||
render(<EffectsOverlay roomWidth={100} />);
|
||||
dis.dispatch({ action: "effects.confetti", event: { getTs: () => eventDate.getTime() } });
|
||||
await waitFor(() => expect(isStarted).toBe(true));
|
||||
});
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<EffectsOverlay/> should render 1`] = `
|
||||
<DocumentFragment>
|
||||
<canvas
|
||||
aria-hidden="true"
|
||||
height="768"
|
||||
style="display: block; z-index: 999999; pointer-events: none; position: fixed; top: 0px; right: 0px;"
|
||||
width="100"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
|
@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
|||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { render, screen, waitFor } from "@testing-library/react";
|
||||
import { MatrixClient, ThreepidMedium } from "matrix-js-sdk/src/matrix";
|
||||
import React from "react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
|
@ -218,7 +218,17 @@ describe("AddRemoveThreepids", () => {
|
|||
await userEvent.type(input, PHONE1_LOCALNUM);
|
||||
|
||||
const addButton = screen.getByRole("button", { name: "Add" });
|
||||
await userEvent.click(addButton);
|
||||
userEvent.click(addButton);
|
||||
|
||||
const continueButton = await screen.findByRole("button", { name: "Continue" });
|
||||
|
||||
await expect(continueButton).toHaveAttribute("aria-disabled", "true");
|
||||
|
||||
await expect(
|
||||
await screen.findByText(
|
||||
`A text message has been sent to +${PHONE1.address}. Please enter the verification code it contains.`,
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(client.requestAdd3pidMsisdnToken).toHaveBeenCalledWith(
|
||||
"GB",
|
||||
|
@ -226,15 +236,14 @@ describe("AddRemoveThreepids", () => {
|
|||
client.generateClientSecret(),
|
||||
1,
|
||||
);
|
||||
const continueButton = screen.getByRole("button", { name: "Continue" });
|
||||
|
||||
expect(continueButton).toHaveAttribute("aria-disabled", "true");
|
||||
|
||||
const verificationInput = screen.getByRole("textbox", { name: "Verification code" });
|
||||
await userEvent.type(verificationInput, "123456");
|
||||
|
||||
expect(continueButton).not.toHaveAttribute("aria-disabled", "true");
|
||||
await userEvent.click(continueButton);
|
||||
userEvent.click(continueButton);
|
||||
|
||||
await waitFor(() => expect(continueButton).toHaveAttribute("aria-disabled", "true"));
|
||||
|
||||
expect(client.addThreePidOnly).toHaveBeenCalledWith({
|
||||
client_secret: client.generateClientSecret(),
|
||||
|
|
|
@ -94,6 +94,10 @@ describe("StopGapWidgetDriver", () => {
|
|||
"org.matrix.msc2762.timeline:!1:example.org",
|
||||
"org.matrix.msc2762.send.event:org.matrix.rageshake_request",
|
||||
"org.matrix.msc2762.receive.event:org.matrix.rageshake_request",
|
||||
"org.matrix.msc2762.send.event:m.reaction",
|
||||
"org.matrix.msc2762.receive.event:m.reaction",
|
||||
"org.matrix.msc2762.send.event:m.room.redaction",
|
||||
"org.matrix.msc2762.receive.event:m.room.redaction",
|
||||
"org.matrix.msc2762.receive.state_event:m.room.create",
|
||||
"org.matrix.msc2762.receive.state_event:m.room.member",
|
||||
"org.matrix.msc2762.receive.state_event:org.matrix.msc3401.call",
|
||||
|
|
311
yarn.lock
311
yarn.lock
|
@ -1515,10 +1515,10 @@
|
|||
minimatch "^3.1.2"
|
||||
strip-json-comments "^3.1.1"
|
||||
|
||||
"@eslint/js@8.57.0":
|
||||
version "8.57.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f"
|
||||
integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==
|
||||
"@eslint/js@8.57.1":
|
||||
version "8.57.1"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2"
|
||||
integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==
|
||||
|
||||
"@floating-ui/core@^1.6.0":
|
||||
version "1.6.5"
|
||||
|
@ -1556,12 +1556,12 @@
|
|||
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.5.tgz#105c37d9d9620ce69b7f692a20c821bf1ad2cbf9"
|
||||
integrity sha512-sTcG+QZ6fdEUObICavU+aB3Mp8HY4n14wYHdxK4fXjPmv3PXZZeY5RaguJmGyeH/CJQhX3fqKUtS4qc1LoHwhQ==
|
||||
|
||||
"@humanwhocodes/config-array@^0.11.14":
|
||||
version "0.11.14"
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b"
|
||||
integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==
|
||||
"@humanwhocodes/config-array@^0.13.0":
|
||||
version "0.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.13.0.tgz#fb907624df3256d04b9aa2df50d7aa97ec648748"
|
||||
integrity sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==
|
||||
dependencies:
|
||||
"@humanwhocodes/object-schema" "^2.0.2"
|
||||
"@humanwhocodes/object-schema" "^2.0.3"
|
||||
debug "^4.3.1"
|
||||
minimatch "^3.0.5"
|
||||
|
||||
|
@ -1570,10 +1570,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
|
||||
integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
|
||||
|
||||
"@humanwhocodes/object-schema@^2.0.2":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917"
|
||||
integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==
|
||||
"@humanwhocodes/object-schema@^2.0.3":
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3"
|
||||
integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==
|
||||
|
||||
"@isaacs/cliui@^8.0.2":
|
||||
version "8.0.2"
|
||||
|
@ -2001,11 +2001,11 @@
|
|||
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
|
||||
|
||||
"@playwright/test@^1.40.1":
|
||||
version "1.46.1"
|
||||
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.46.1.tgz#a8dfdcd623c4c23bb1b7ea588058aad41055c188"
|
||||
integrity sha512-Fq6SwLujA/DOIvNC2EL/SojJnkKf/rAwJ//APpJJHRyMi1PdKrY3Az+4XNQ51N4RTbItbIByQ0jgd1tayq1aeA==
|
||||
version "1.47.1"
|
||||
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.47.1.tgz#568a46229a5aef54b74977297a7946bb5ac4b67b"
|
||||
integrity sha512-dbWpcNQZ5nj16m+A5UNScYx7HX5trIy7g4phrcitn+Nk83S32EBX/CLU4hiF4RGKX/yRc93AAqtfaXB7JWBd4Q==
|
||||
dependencies:
|
||||
playwright "1.46.1"
|
||||
playwright "1.47.1"
|
||||
|
||||
"@radix-ui/primitive@1.0.1":
|
||||
version "1.0.1"
|
||||
|
@ -2347,76 +2347,76 @@
|
|||
resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.1.0.tgz#f817d1d3265ac5415dadc67edab30ae196696438"
|
||||
integrity sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==
|
||||
|
||||
"@sentry-internal/browser-utils@8.28.0":
|
||||
version "8.28.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.28.0.tgz#bddc58c154e898195d45e971e058e237085bbcc2"
|
||||
integrity sha512-tE9++KEy8SlqibTmYymuxFVAnutsXBqrwQ936WJbjaMfkqXiro7C1El0ybkprskd0rKS7kln20Q6nQlNlMEoTA==
|
||||
"@sentry-internal/browser-utils@8.30.0":
|
||||
version "8.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.30.0.tgz#eb68c79556ffb864eb5924a53affde52f2b77362"
|
||||
integrity sha512-pwX+awNWaxSOAsBLVLqc1+Hw+Fm1Nci9mbKFA6Ed5YzCG049PnBVQwugpmx2dcyyCqJpORhcIqb9jHdCkYmCiA==
|
||||
dependencies:
|
||||
"@sentry/core" "8.28.0"
|
||||
"@sentry/types" "8.28.0"
|
||||
"@sentry/utils" "8.28.0"
|
||||
"@sentry/core" "8.30.0"
|
||||
"@sentry/types" "8.30.0"
|
||||
"@sentry/utils" "8.30.0"
|
||||
|
||||
"@sentry-internal/feedback@8.28.0":
|
||||
version "8.28.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.28.0.tgz#f278548ead037ad38e54d24b1afcdc1f711c5715"
|
||||
integrity sha512-5vYunPCDBLCJ8QNnhepacdYheiN+UtYxpGAIaC/zjBC1nDuBgWs+TfKPo1UlO/1sesfgs9ibpxtShOweucL61g==
|
||||
"@sentry-internal/feedback@8.30.0":
|
||||
version "8.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.30.0.tgz#6f78a245298502e4cc5ce77313dde6965abfecfe"
|
||||
integrity sha512-ParFRxQY6helxkwUDmro77Wc5uSIC6rZos88jYMrYwFmoTJaNWf4lDzPyECfdSiSYyzSMZk4dorSUN85Ul7DCg==
|
||||
dependencies:
|
||||
"@sentry/core" "8.28.0"
|
||||
"@sentry/types" "8.28.0"
|
||||
"@sentry/utils" "8.28.0"
|
||||
"@sentry/core" "8.30.0"
|
||||
"@sentry/types" "8.30.0"
|
||||
"@sentry/utils" "8.30.0"
|
||||
|
||||
"@sentry-internal/replay-canvas@8.28.0":
|
||||
version "8.28.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.28.0.tgz#6a08541f9fecd912b7334c693a403469c9e34a89"
|
||||
integrity sha512-RfpYHDHMUKGeEdx41QtHITjEn6P3tGaDPHvatqdrD3yv4j+wbJ6laX1PrIxCpGFUtjdzkqi/KUcvUd2kzbH/FA==
|
||||
"@sentry-internal/replay-canvas@8.30.0":
|
||||
version "8.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.30.0.tgz#3630eec14d23b1fd368d8c331ee695aa5bb41425"
|
||||
integrity sha512-y/QqcvchhtMlVA6eOZicIfTxtZarazQZJuFW0018ynPxBTiuuWSxMCLqduulXUYsFejfD8/eKHb3BpCIFdDYjg==
|
||||
dependencies:
|
||||
"@sentry-internal/replay" "8.28.0"
|
||||
"@sentry/core" "8.28.0"
|
||||
"@sentry/types" "8.28.0"
|
||||
"@sentry/utils" "8.28.0"
|
||||
"@sentry-internal/replay" "8.30.0"
|
||||
"@sentry/core" "8.30.0"
|
||||
"@sentry/types" "8.30.0"
|
||||
"@sentry/utils" "8.30.0"
|
||||
|
||||
"@sentry-internal/replay@8.28.0":
|
||||
version "8.28.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.28.0.tgz#a84523066ab363239ef6b4180726908cab510e5f"
|
||||
integrity sha512-70jvzzOL5O74gahgXKyRkZgiYN93yly5gq+bbj4/6NRQ+EtPd285+ccy0laExdfyK0ugvvwD4v+1MQit52OAsg==
|
||||
"@sentry-internal/replay@8.30.0":
|
||||
version "8.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.30.0.tgz#6a4a8bd551a16ea5f77f913acbccd88061868c84"
|
||||
integrity sha512-/KFre+BrovPCiovgAu5N1ErJtkDVzkJA5hV3Jw011AlxRWxrmPwu6+9sV9/rn3tqYAGyq6IggYqeIOHhLh1Ihg==
|
||||
dependencies:
|
||||
"@sentry-internal/browser-utils" "8.28.0"
|
||||
"@sentry/core" "8.28.0"
|
||||
"@sentry/types" "8.28.0"
|
||||
"@sentry/utils" "8.28.0"
|
||||
"@sentry-internal/browser-utils" "8.30.0"
|
||||
"@sentry/core" "8.30.0"
|
||||
"@sentry/types" "8.30.0"
|
||||
"@sentry/utils" "8.30.0"
|
||||
|
||||
"@sentry/browser@^8.0.0":
|
||||
version "8.28.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.28.0.tgz#e3d28e7a917c212418c887b4fabacc6ed88baea8"
|
||||
integrity sha512-i/gjMYzIGQiPFH1pCbdnTwH9xs9mTAqzN+goP3GWX5a58frc7h8vxyA/5z0yMd0aCW6U8mVxnoAT72vGbKbx0g==
|
||||
version "8.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.30.0.tgz#3c6d5ef62d7daca2873b47f59b136c33941b56de"
|
||||
integrity sha512-M+tKqawH9S3CqlAIcqdZcHbcsNQkEa9MrPqPCYvXco3C4LRpNizJP2XwBiGQY2yK+fOSvbaWpPtlI938/wuRZQ==
|
||||
dependencies:
|
||||
"@sentry-internal/browser-utils" "8.28.0"
|
||||
"@sentry-internal/feedback" "8.28.0"
|
||||
"@sentry-internal/replay" "8.28.0"
|
||||
"@sentry-internal/replay-canvas" "8.28.0"
|
||||
"@sentry/core" "8.28.0"
|
||||
"@sentry/types" "8.28.0"
|
||||
"@sentry/utils" "8.28.0"
|
||||
"@sentry-internal/browser-utils" "8.30.0"
|
||||
"@sentry-internal/feedback" "8.30.0"
|
||||
"@sentry-internal/replay" "8.30.0"
|
||||
"@sentry-internal/replay-canvas" "8.30.0"
|
||||
"@sentry/core" "8.30.0"
|
||||
"@sentry/types" "8.30.0"
|
||||
"@sentry/utils" "8.30.0"
|
||||
|
||||
"@sentry/core@8.28.0":
|
||||
version "8.28.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.28.0.tgz#dd28fa913c296b443d4070f147c63e81edf429c8"
|
||||
integrity sha512-+If9uubvpZpvaQQw4HLiKPhrSS9/KcoA/AcdQkNm+5CVwAoOmDPtyYfkPBgfo2hLZnZQqR1bwkz/PrNoOm+gqA==
|
||||
"@sentry/core@8.30.0":
|
||||
version "8.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.30.0.tgz#f929e42e9a537bfa3eb6024082714e9ab98d822b"
|
||||
integrity sha512-CJ/FuWLw0QEKGKXGL/nm9eaOdajEcmPekLuHAuOCxID7N07R9l9laz3vFbAkUZ97GGDv3sYrJZgywfY3Moropg==
|
||||
dependencies:
|
||||
"@sentry/types" "8.28.0"
|
||||
"@sentry/utils" "8.28.0"
|
||||
"@sentry/types" "8.30.0"
|
||||
"@sentry/utils" "8.30.0"
|
||||
|
||||
"@sentry/types@8.28.0":
|
||||
version "8.28.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.28.0.tgz#a1cfc004d5714679cb3fed06c27298b0275d13b5"
|
||||
integrity sha512-hOfqfd92/AzBrEdMgmmV1VfOXJbIfleFTnerRl0mg/+CcNgP/6+Fdonp354TD56ouWNF2WkOM6sEKSXMWp6SEQ==
|
||||
"@sentry/types@8.30.0":
|
||||
version "8.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.30.0.tgz#5f5011f5b16bafd30a039ca5e8c337e948c703fb"
|
||||
integrity sha512-kgWW2BCjBmVlSQRG32GonHEVyeDbys74xf9mLPvynwHTgw3+NUlNAlEdu05xnb2ow4bCTHfbkS5G1zRgyv5k4Q==
|
||||
|
||||
"@sentry/utils@8.28.0":
|
||||
version "8.28.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.28.0.tgz#0feb46015033879b2a3cee4c0661386610025f47"
|
||||
integrity sha512-smhk7PJpvDMQ2DB5p2qn9UeoUHdU41IgjMmS2xklZpa8tjzBTxDeWpGvrX2fuH67D9bAJuLC/XyZjJCHLoEW5g==
|
||||
"@sentry/utils@8.30.0":
|
||||
version "8.30.0"
|
||||
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.30.0.tgz#2343dd8593ea83890b3e0d792ed3fa257955a26b"
|
||||
integrity sha512-wZxU2HWlzsnu8214Xy7S7cRIuD6h8Z5DnnkojJfX0i0NLooepZQk2824el1Q13AakLb7/S8CHSHXOMnCtoSduw==
|
||||
dependencies:
|
||||
"@sentry/types" "8.28.0"
|
||||
"@sentry/types" "8.30.0"
|
||||
|
||||
"@sinclair/typebox@^0.27.8":
|
||||
version "0.27.8"
|
||||
|
@ -2784,9 +2784,9 @@
|
|||
undici-types "~5.26.4"
|
||||
|
||||
"@types/node@18":
|
||||
version "18.19.48"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.48.tgz#3a1696f4a7298d8831ed9ce47db62bf4c62c8880"
|
||||
integrity sha512-7WevbG4ekUcRQSZzOwxWgi5dZmTak7FaxXDoW7xVxPBmKx1rTzfmRLkeCgJzcbBnOV2dkhAPc8cCeT6agocpjg==
|
||||
version "18.19.50"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.50.tgz#8652b34ee7c0e7e2004b3f08192281808d41bf5a"
|
||||
integrity sha512-xonK+NRrMBRtkL1hVCc3G+uXtjh1Al4opBLjqVmipe5ZAaBYWW6cNAiBVZ1BvmkBhep698rP3UM3aRAdSALuhg==
|
||||
dependencies:
|
||||
undici-types "~5.26.4"
|
||||
|
||||
|
@ -2858,10 +2858,10 @@
|
|||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react@*", "@types/react@17.0.80", "@types/react@^17":
|
||||
version "17.0.80"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.80.tgz#a5dfc351d6a41257eb592d73d3a85d3b7dbcbb41"
|
||||
integrity sha512-LrgHIu2lEtIo8M7d1FcI3BdwXWoRQwMoXOZ7+dPTW0lYREjmlHl3P0U1VD0i/9tppOuv8/sam7sOjx34TxSFbA==
|
||||
"@types/react@*", "@types/react@17.0.82", "@types/react@^17":
|
||||
version "17.0.82"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.82.tgz#eb84c38ee1023cd61be1b909fde083ac83fc163f"
|
||||
integrity sha512-wTW8Lu/PARGPFE8tOZqCvprOKg5sen/2uS03yKn2xbCDFP9oLncm7vMDQ2+dEQXHVIXrOpW6u72xUXEXO0ypSw==
|
||||
dependencies:
|
||||
"@types/prop-types" "*"
|
||||
"@types/scheduler" "^0.16"
|
||||
|
@ -3601,10 +3601,10 @@ blurhash@^2.0.3:
|
|||
resolved "https://registry.yarnpkg.com/blurhash/-/blurhash-2.0.5.tgz#efde729fc14a2f03571a6aa91b49cba80d1abe4b"
|
||||
integrity sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w==
|
||||
|
||||
body-parser@1.20.2:
|
||||
version "1.20.2"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd"
|
||||
integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==
|
||||
body-parser@1.20.3:
|
||||
version "1.20.3"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6"
|
||||
integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==
|
||||
dependencies:
|
||||
bytes "3.1.2"
|
||||
content-type "~1.0.5"
|
||||
|
@ -3614,7 +3614,7 @@ body-parser@1.20.2:
|
|||
http-errors "2.0.0"
|
||||
iconv-lite "0.4.24"
|
||||
on-finished "2.4.1"
|
||||
qs "6.11.0"
|
||||
qs "6.13.0"
|
||||
raw-body "2.5.2"
|
||||
type-is "~1.6.18"
|
||||
unpipe "1.0.0"
|
||||
|
@ -4059,6 +4059,14 @@ css-tree@2.3.1, css-tree@^2.3.1:
|
|||
mdn-data "2.0.30"
|
||||
source-map-js "^1.0.1"
|
||||
|
||||
css-tree@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-3.0.0.tgz#079c7b87e465a28cedbc826502f9a227213db0f3"
|
||||
integrity sha512-o88DVQ6GzsABn1+6+zo2ct801dBO5OASVyxbbvA2W20ue2puSh/VOuqUj90eUeMSX/xqGqBmOKiRQN7tJOuBXw==
|
||||
dependencies:
|
||||
mdn-data "2.10.0"
|
||||
source-map-js "^1.0.1"
|
||||
|
||||
css.escape@^1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
|
||||
|
@ -4443,6 +4451,11 @@ encodeurl@~1.0.2:
|
|||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
|
||||
|
||||
encodeurl@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58"
|
||||
integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
|
||||
|
||||
entities@^4.2.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174"
|
||||
|
@ -4844,16 +4857,16 @@ eslint-visitor-keys@^4.0.0:
|
|||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz#e3adc021aa038a2a8e0b2f8b0ce8f66b9483b1fb"
|
||||
integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==
|
||||
|
||||
eslint@8.57.0:
|
||||
version "8.57.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668"
|
||||
integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==
|
||||
eslint@8.57.1:
|
||||
version "8.57.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9"
|
||||
integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.2.0"
|
||||
"@eslint-community/regexpp" "^4.6.1"
|
||||
"@eslint/eslintrc" "^2.1.4"
|
||||
"@eslint/js" "8.57.0"
|
||||
"@humanwhocodes/config-array" "^0.11.14"
|
||||
"@eslint/js" "8.57.1"
|
||||
"@humanwhocodes/config-array" "^0.13.0"
|
||||
"@humanwhocodes/module-importer" "^1.0.1"
|
||||
"@nodelib/fs.walk" "^1.2.8"
|
||||
"@ungap/structured-clone" "^1.2.0"
|
||||
|
@ -5009,36 +5022,36 @@ expect@^29.0.0, expect@^29.7.0:
|
|||
jest-util "^29.7.0"
|
||||
|
||||
express@^4.18.2:
|
||||
version "4.19.2"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.19.2.tgz#e25437827a3aa7f2a827bc8171bbbb664a356465"
|
||||
integrity sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==
|
||||
version "4.20.0"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.20.0.tgz#f1d08e591fcec770c07be4767af8eb9bcfd67c48"
|
||||
integrity sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==
|
||||
dependencies:
|
||||
accepts "~1.3.8"
|
||||
array-flatten "1.1.1"
|
||||
body-parser "1.20.2"
|
||||
body-parser "1.20.3"
|
||||
content-disposition "0.5.4"
|
||||
content-type "~1.0.4"
|
||||
cookie "0.6.0"
|
||||
cookie-signature "1.0.6"
|
||||
debug "2.6.9"
|
||||
depd "2.0.0"
|
||||
encodeurl "~1.0.2"
|
||||
encodeurl "~2.0.0"
|
||||
escape-html "~1.0.3"
|
||||
etag "~1.8.1"
|
||||
finalhandler "1.2.0"
|
||||
fresh "0.5.2"
|
||||
http-errors "2.0.0"
|
||||
merge-descriptors "1.0.1"
|
||||
merge-descriptors "1.0.3"
|
||||
methods "~1.1.2"
|
||||
on-finished "2.4.1"
|
||||
parseurl "~1.3.3"
|
||||
path-to-regexp "0.1.7"
|
||||
path-to-regexp "0.1.10"
|
||||
proxy-addr "~2.0.7"
|
||||
qs "6.11.0"
|
||||
range-parser "~1.2.1"
|
||||
safe-buffer "5.2.1"
|
||||
send "0.18.0"
|
||||
serve-static "1.15.0"
|
||||
send "0.19.0"
|
||||
serve-static "1.16.0"
|
||||
setprototypeof "1.2.0"
|
||||
statuses "2.0.1"
|
||||
type-is "~1.6.18"
|
||||
|
@ -7065,6 +7078,11 @@ mdn-data@2.0.30:
|
|||
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc"
|
||||
integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==
|
||||
|
||||
mdn-data@2.10.0:
|
||||
version "2.10.0"
|
||||
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.10.0.tgz#701da407f8fbc7a42aa0ba0c149ec897daef8986"
|
||||
integrity sha512-qq7C3EtK3yJXMwz1zAab65pjl+UhohqMOctTgcqjLOWABqmwj+me02LSsCuEUxnst9X1lCBpoE0WArGKgdGDzw==
|
||||
|
||||
mdurl@~1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
|
||||
|
@ -7090,10 +7108,10 @@ meow@^13.2.0:
|
|||
resolved "https://registry.yarnpkg.com/meow/-/meow-13.2.0.tgz#6b7d63f913f984063b3cc261b6e8800c4cd3474f"
|
||||
integrity sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==
|
||||
|
||||
merge-descriptors@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
|
||||
integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
|
||||
merge-descriptors@1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5"
|
||||
integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
|
||||
|
||||
merge-stream@^2.0.0:
|
||||
version "2.0.0"
|
||||
|
@ -7570,10 +7588,10 @@ path-scurry@^2.0.0:
|
|||
lru-cache "^11.0.0"
|
||||
minipass "^7.1.2"
|
||||
|
||||
path-to-regexp@0.1.7:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
|
||||
integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
|
||||
path-to-regexp@0.1.10:
|
||||
version "0.1.10"
|
||||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b"
|
||||
integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==
|
||||
|
||||
path-to-regexp@^2.2.1:
|
||||
version "2.4.0"
|
||||
|
@ -7632,17 +7650,17 @@ pkg-dir@^4.2.0:
|
|||
dependencies:
|
||||
find-up "^4.0.0"
|
||||
|
||||
playwright-core@1.46.1, playwright-core@^1.45.1:
|
||||
version "1.46.1"
|
||||
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.46.1.tgz#28f3ab35312135dda75b0c92a3e5c0e7edb9cc8b"
|
||||
integrity sha512-h9LqIQaAv+CYvWzsZ+h3RsrqCStkBHlgo6/TJlFst3cOTlLghBQlJwPOZKQJTKNaD3QIB7aAVQ+gfWbN3NXB7A==
|
||||
playwright-core@1.47.1, playwright-core@^1.45.1:
|
||||
version "1.47.1"
|
||||
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.47.1.tgz#bb45bdfb0d48412c535501aa3805867282857df8"
|
||||
integrity sha512-i1iyJdLftqtt51mEk6AhYFaAJCDx0xQ/O5NU8EKaWFgMjItPVma542Nh/Aq8aLCjIJSzjaiEQGW/nyqLkGF1OQ==
|
||||
|
||||
playwright@1.46.1:
|
||||
version "1.46.1"
|
||||
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.46.1.tgz#ea562bc48373648e10420a10c16842f0b227c218"
|
||||
integrity sha512-oPcr1yqoXLCkgKtD5eNUPLiN40rYEM39odNpIb6VE6S7/15gJmA1NzVv6zJYusV0e7tzvkU/utBFNa/Kpxmwng==
|
||||
playwright@1.47.1:
|
||||
version "1.47.1"
|
||||
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.47.1.tgz#cdc1116f5265b8d2ff7be0d8942d49900634dc6c"
|
||||
integrity sha512-SUEKi6947IqYbKxRiqnbUobVZY4bF1uu+ZnZNJX9DfU1tlf2UhWfvVjLf01pQx9URsOr18bFVUKXmanYWhbfkw==
|
||||
dependencies:
|
||||
playwright-core "1.46.1"
|
||||
playwright-core "1.47.1"
|
||||
optionalDependencies:
|
||||
fsevents "2.3.2"
|
||||
|
||||
|
@ -7678,7 +7696,7 @@ postcss-media-query-parser@^0.2.3:
|
|||
resolved "https://registry.yarnpkg.com/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz#27b39c6f4d94f81b1a73b8f76351c609e5cef244"
|
||||
integrity sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==
|
||||
|
||||
postcss-resolve-nested-selector@^0.1.4, postcss-resolve-nested-selector@^0.1.6:
|
||||
postcss-resolve-nested-selector@^0.1.6:
|
||||
version "0.1.6"
|
||||
resolved "https://registry.yarnpkg.com/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz#3d84dec809f34de020372c41b039956966896686"
|
||||
integrity sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==
|
||||
|
@ -7693,7 +7711,7 @@ postcss-scss@^4.0.4:
|
|||
resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-4.0.9.tgz#a03c773cd4c9623cb04ce142a52afcec74806685"
|
||||
integrity sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==
|
||||
|
||||
postcss-selector-parser@^6.1.1, postcss-selector-parser@^6.1.2:
|
||||
postcss-selector-parser@^6.1.2:
|
||||
version "6.1.2"
|
||||
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de"
|
||||
integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==
|
||||
|
@ -7854,6 +7872,13 @@ qs@6.11.0:
|
|||
dependencies:
|
||||
side-channel "^1.0.4"
|
||||
|
||||
qs@6.13.0:
|
||||
version "6.13.0"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
|
||||
integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==
|
||||
dependencies:
|
||||
side-channel "^1.0.6"
|
||||
|
||||
querystring@^0.2.0:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd"
|
||||
|
@ -8445,10 +8470,29 @@ send@0.18.0:
|
|||
range-parser "~1.2.1"
|
||||
statuses "2.0.1"
|
||||
|
||||
serve-static@1.15.0:
|
||||
version "1.15.0"
|
||||
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
|
||||
integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
|
||||
send@0.19.0:
|
||||
version "0.19.0"
|
||||
resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8"
|
||||
integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==
|
||||
dependencies:
|
||||
debug "2.6.9"
|
||||
depd "2.0.0"
|
||||
destroy "1.2.0"
|
||||
encodeurl "~1.0.2"
|
||||
escape-html "~1.0.3"
|
||||
etag "~1.8.1"
|
||||
fresh "0.5.2"
|
||||
http-errors "2.0.0"
|
||||
mime "1.6.0"
|
||||
ms "2.1.3"
|
||||
on-finished "2.4.1"
|
||||
range-parser "~1.2.1"
|
||||
statuses "2.0.1"
|
||||
|
||||
serve-static@1.16.0:
|
||||
version "1.16.0"
|
||||
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.0.tgz#2bf4ed49f8af311b519c46f272bf6ac3baf38a92"
|
||||
integrity sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==
|
||||
dependencies:
|
||||
encodeurl "~1.0.2"
|
||||
escape-html "~1.0.3"
|
||||
|
@ -8571,16 +8615,21 @@ slice-ansi@^7.1.0:
|
|||
ansi-styles "^6.2.1"
|
||||
is-fullwidth-code-point "^5.0.0"
|
||||
|
||||
source-map-js@^1.0.1, source-map-js@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af"
|
||||
integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==
|
||||
source-map-js@^1.0.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
|
||||
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
||||
|
||||
source-map-js@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
||||
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
|
||||
|
||||
source-map-js@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af"
|
||||
integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==
|
||||
|
||||
source-map-support@0.5.13:
|
||||
version "0.5.13"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932"
|
||||
|
@ -8841,16 +8890,16 @@ stylelint-config-standard@^36.0.0:
|
|||
stylelint-config-recommended "^14.0.1"
|
||||
|
||||
stylelint-scss@^6.0.0:
|
||||
version "6.5.1"
|
||||
resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-6.5.1.tgz#bcb6a4ada71a0adbf181e155548e5f25ee4aeece"
|
||||
integrity sha512-ZLqdqihm6uDYkrsOeD6YWb+stZI8Wn92kUNDhE4M+g9g1aCnRv0JlOrttFiAJJwaNzpdQgX3YJb5vDQXVuO9Ww==
|
||||
version "6.7.0"
|
||||
resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-6.7.0.tgz#df9c2bcd20c555c5670914f23bb983c94bfbb0e3"
|
||||
integrity sha512-RFIa2A+pVWS5wjNT+whtK7wsbZEWazyqesCuSaPbPlZ8lh2TujwVJSnCYJijg6ChZzwI8pZPRZS1L6A9aCbXDg==
|
||||
dependencies:
|
||||
css-tree "2.3.1"
|
||||
is-plain-object "5.0.0"
|
||||
known-css-properties "^0.34.0"
|
||||
postcss-media-query-parser "^0.2.3"
|
||||
postcss-resolve-nested-selector "^0.1.4"
|
||||
postcss-selector-parser "^6.1.1"
|
||||
postcss-resolve-nested-selector "^0.1.6"
|
||||
postcss-selector-parser "^6.1.2"
|
||||
postcss-value-parser "^4.2.0"
|
||||
|
||||
stylelint@^16.1.0:
|
||||
|
|
Loading…
Reference in a new issue