Simplify registration with email validation (#11398)
parent
beafe686a9
commit
0842559fb2
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
import { HomeserverInstance } from "../../plugins/utils/homeserver";
|
||||
import { Mailhog } from "../../support/mailhog";
|
||||
|
||||
describe("Email Registration", () => {
|
||||
let homeserver: HomeserverInstance;
|
||||
let mailhog: Mailhog;
|
||||
|
||||
beforeEach(() => {
|
||||
cy.startMailhog().then((_mailhog) => {
|
||||
mailhog = _mailhog;
|
||||
cy.startHomeserver({
|
||||
template: "email",
|
||||
variables: {
|
||||
SMTP_HOST: "host.docker.internal",
|
||||
SMTP_PORT: _mailhog.instance.smtpPort,
|
||||
},
|
||||
}).then((_homeserver) => {
|
||||
homeserver = _homeserver;
|
||||
|
||||
cy.intercept(
|
||||
{ method: "GET", pathname: "/config.json" },
|
||||
{
|
||||
body: {
|
||||
default_server_config: {
|
||||
"m.homeserver": {
|
||||
base_url: homeserver.baseUrl,
|
||||
},
|
||||
"m.identity_server": {
|
||||
base_url: "https://server.invalid",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
cy.visit("/#/register");
|
||||
cy.injectAxe();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cy.stopHomeserver(homeserver);
|
||||
cy.stopMailhog(mailhog);
|
||||
});
|
||||
|
||||
it("registers an account and lands on the use case selection screen", () => {
|
||||
cy.findByRole("textbox", { name: "Username" }).should("be.visible");
|
||||
// Hide the server text as it contains the randomly allocated Homeserver port
|
||||
const percyCSS = ".mx_ServerPicker_server { visibility: hidden !important; }";
|
||||
|
||||
cy.findByRole("textbox", { name: "Username" }).type("alice");
|
||||
cy.findByPlaceholderText("Password").type("totally a great password");
|
||||
cy.findByPlaceholderText("Confirm password").type("totally a great password");
|
||||
cy.findByPlaceholderText("Email").type("alice@email.com");
|
||||
cy.findByRole("button", { name: "Register" }).click();
|
||||
|
||||
cy.findByText("Check your email to continue").should("be.visible");
|
||||
cy.percySnapshot("Registration check your email", { percyCSS });
|
||||
cy.checkA11y();
|
||||
|
||||
cy.findByText("An error was encountered when sending the email").should("not.exist");
|
||||
|
||||
cy.waitForPromise(async () => {
|
||||
const messages = await mailhog.api.messages();
|
||||
expect(messages.items).to.have.length(1);
|
||||
expect(messages.items[0].to).to.eq("alice@email.com");
|
||||
const [link] = messages.items[0].text.match(/http.+/);
|
||||
return link;
|
||||
}).as("emailLink");
|
||||
|
||||
cy.get<string>("@emailLink").then((link) => cy.request(link));
|
||||
|
||||
cy.get(".mx_UseCaseSelection_skip", { timeout: 30000 }).should("exist");
|
||||
});
|
||||
});
|
|
@ -26,6 +26,7 @@ import { webserver } from "./webserver";
|
|||
import { docker } from "./docker";
|
||||
import { log } from "./log";
|
||||
import { oAuthServer } from "./oauth_server";
|
||||
import { mailhogDocker } from "./mailhog";
|
||||
|
||||
/**
|
||||
* @type {Cypress.PluginConfig}
|
||||
|
@ -41,4 +42,5 @@ export default function (on: PluginEvents, config: PluginConfigOptions) {
|
|||
installLogsPrinter(on, {
|
||||
// printLogsToConsole: "always",
|
||||
});
|
||||
mailhogDocker(on, config);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
import PluginEvents = Cypress.PluginEvents;
|
||||
import PluginConfigOptions = Cypress.PluginConfigOptions;
|
||||
import { getFreePort } from "../utils/port";
|
||||
import { dockerIp, dockerRun, dockerStop } from "../docker";
|
||||
|
||||
// A cypress plugins to add command to manage an instance of Mailhog in Docker
|
||||
|
||||
export interface Instance {
|
||||
host: string;
|
||||
smtpPort: number;
|
||||
httpPort: number;
|
||||
containerId: string;
|
||||
}
|
||||
|
||||
const instances = new Map<string, Instance>();
|
||||
|
||||
// Start a synapse instance: the template must be the name of
|
||||
// one of the templates in the cypress/plugins/synapsedocker/templates
|
||||
// directory
|
||||
async function mailhogStart(): Promise<Instance> {
|
||||
const smtpPort = await getFreePort();
|
||||
const httpPort = await getFreePort();
|
||||
|
||||
console.log(`Starting mailhog...`);
|
||||
|
||||
const containerId = await dockerRun({
|
||||
image: "mailhog/mailhog:latest",
|
||||
containerName: `react-sdk-cypress-mailhog`,
|
||||
params: ["--rm", "-p", `${smtpPort}:1025/tcp`, "-p", `${httpPort}:8025/tcp`],
|
||||
});
|
||||
|
||||
console.log(`Started mailhog on ports smtp=${smtpPort} http=${httpPort}.`);
|
||||
|
||||
const host = await dockerIp({ containerId });
|
||||
const instance: Instance = { smtpPort, httpPort, containerId, host };
|
||||
instances.set(containerId, instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
async function mailhogStop(id: string): Promise<void> {
|
||||
const synCfg = instances.get(id);
|
||||
|
||||
if (!synCfg) throw new Error("Unknown mailhog ID");
|
||||
|
||||
await dockerStop({
|
||||
containerId: id,
|
||||
});
|
||||
|
||||
instances.delete(id);
|
||||
|
||||
console.log(`Stopped mailhog id ${id}.`);
|
||||
// cypress deliberately fails if you return 'undefined', so
|
||||
// return null to signal all is well, and we've handled the task.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Cypress.PluginConfig}
|
||||
*/
|
||||
export function mailhogDocker(on: PluginEvents, config: PluginConfigOptions) {
|
||||
on("task", {
|
||||
mailhogStart,
|
||||
mailhogStop,
|
||||
});
|
||||
|
||||
on("after:spec", async (spec) => {
|
||||
// Cleans up any remaining instances after a spec run
|
||||
for (const synId of instances.keys()) {
|
||||
console.warn(`Cleaning up synapse ID ${synId} after ${spec.name}`);
|
||||
await mailhogStop(synId);
|
||||
}
|
||||
});
|
||||
}
|
|
@ -65,6 +65,12 @@ async function cfgDirFromTemplate(opts: StartHomeserverOpts): Promise<Homeserver
|
|||
hsYaml = hsYaml.replace(/{{FORM_SECRET}}/g, formSecret);
|
||||
hsYaml = hsYaml.replace(/{{PUBLIC_BASEURL}}/g, baseUrl);
|
||||
hsYaml = hsYaml.replace(/{{OAUTH_SERVER_PORT}}/g, opts.oAuthServerPort?.toString());
|
||||
if (opts.variables) {
|
||||
for (const key in opts.variables) {
|
||||
hsYaml = hsYaml.replace(new RegExp("%" + key + "%", "g"), String(opts.variables[key]));
|
||||
}
|
||||
}
|
||||
|
||||
await fse.writeFile(path.join(tempDir, "homeserver.yaml"), hsYaml);
|
||||
|
||||
// now generate a signing key (we could use synapse's config generation for
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
A synapse configured to require an email for registration
|
|
@ -0,0 +1,44 @@
|
|||
server_name: "localhost"
|
||||
pid_file: /data/homeserver.pid
|
||||
public_baseurl: "{{PUBLIC_BASEURL}}"
|
||||
listeners:
|
||||
- port: 8008
|
||||
tls: false
|
||||
bind_addresses: ["::"]
|
||||
type: http
|
||||
x_forwarded: true
|
||||
|
||||
resources:
|
||||
- names: [client]
|
||||
compress: false
|
||||
|
||||
database:
|
||||
name: "sqlite3"
|
||||
args:
|
||||
database: ":memory:"
|
||||
|
||||
log_config: "/data/log.config"
|
||||
|
||||
media_store_path: "/data/media_store"
|
||||
uploads_path: "/data/uploads"
|
||||
enable_registration: true
|
||||
registrations_require_3pid:
|
||||
- email
|
||||
registration_shared_secret: "{{REGISTRATION_SECRET}}"
|
||||
report_stats: false
|
||||
macaroon_secret_key: "{{MACAROON_SECRET_KEY}}"
|
||||
form_secret: "{{FORM_SECRET}}"
|
||||
signing_key_path: "/data/localhost.signing.key"
|
||||
|
||||
trusted_key_servers:
|
||||
- server_name: "matrix.org"
|
||||
suppress_key_server_warning: true
|
||||
|
||||
ui_auth:
|
||||
session_timeout: "300s"
|
||||
|
||||
email:
|
||||
smtp_host: "%SMTP_HOST%"
|
||||
smtp_port: %SMTP_PORT%
|
||||
notif_from: "Your Friendly %(app)s homeserver <noreply@example.com>"
|
||||
app_name: my_branded_matrix_server
|
|
@ -0,0 +1,50 @@
|
|||
# Log configuration for Synapse.
|
||||
#
|
||||
# This is a YAML file containing a standard Python logging configuration
|
||||
# dictionary. See [1] for details on the valid settings.
|
||||
#
|
||||
# Synapse also supports structured logging for machine readable logs which can
|
||||
# be ingested by ELK stacks. See [2] for details.
|
||||
#
|
||||
# [1]: https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema
|
||||
# [2]: https://matrix-org.github.io/synapse/latest/structured_logging.html
|
||||
|
||||
version: 1
|
||||
|
||||
formatters:
|
||||
precise:
|
||||
format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s'
|
||||
|
||||
handlers:
|
||||
# A handler that writes logs to stderr. Unused by default, but can be used
|
||||
# instead of "buffer" and "file" in the logger handlers.
|
||||
console:
|
||||
class: logging.StreamHandler
|
||||
formatter: precise
|
||||
|
||||
loggers:
|
||||
synapse.storage.SQL:
|
||||
# beware: increasing this to DEBUG will make synapse log sensitive
|
||||
# information such as access tokens.
|
||||
level: INFO
|
||||
|
||||
twisted:
|
||||
# We send the twisted logging directly to the file handler,
|
||||
# to work around https://github.com/matrix-org/synapse/issues/3471
|
||||
# when using "buffer" logger. Use "console" to log to stderr instead.
|
||||
handlers: [console]
|
||||
propagate: false
|
||||
|
||||
root:
|
||||
level: INFO
|
||||
|
||||
# Write logs to the `buffer` handler, which will buffer them together in memory,
|
||||
# then write them to a file.
|
||||
#
|
||||
# Replace "buffer" with "console" to log to stderr instead. (Note that you'll
|
||||
# also need to update the configuration for the `twisted` logger above, in
|
||||
# this case.)
|
||||
#
|
||||
handlers: [console]
|
||||
|
||||
disable_existing_loggers: false
|
|
@ -40,6 +40,8 @@ import "./network";
|
|||
import "./composer";
|
||||
import "./proxy";
|
||||
import "./axe";
|
||||
import "./mailhog";
|
||||
import "./promise";
|
||||
|
||||
installLogsCollector({
|
||||
// specify the types of logs to collect (and report to the node console at the end of the test)
|
||||
|
|
|
@ -28,6 +28,9 @@ export interface StartHomeserverOpts {
|
|||
|
||||
/** Port of an OAuth server to configure the homeserver to use */
|
||||
oAuthServerPort?: number;
|
||||
|
||||
/** Additional variables to inject into the configuration template **/
|
||||
variables?: Record<string, string | number>;
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
import mailhog from "mailhog";
|
||||
|
||||
import Chainable = Cypress.Chainable;
|
||||
import { Instance } from "../plugins/mailhog";
|
||||
|
||||
export interface Mailhog {
|
||||
api: mailhog.API;
|
||||
instance: Instance;
|
||||
}
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
startMailhog(): Chainable<Mailhog>;
|
||||
stopMailhog(instance: Mailhog): Chainable<void>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Cypress.Commands.add("startMailhog", (): Chainable<Mailhog> => {
|
||||
return cy.task<Instance>("mailhogStart", { log: false }).then((x) => {
|
||||
Cypress.log({ name: "startHomeserver", message: `Started mailhog instance ${x.containerId}` });
|
||||
return {
|
||||
api: mailhog({
|
||||
host: "localhost",
|
||||
port: x.httpPort,
|
||||
}),
|
||||
instance: x,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add("stopMailhog", (mailhog: Mailhog): Chainable<void> => {
|
||||
return cy.task("mailhogStop", mailhog.instance.containerId);
|
||||
});
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/// <reference types="cypress" />
|
||||
|
||||
import Chainable = Cypress.Chainable;
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
interface Chainable {
|
||||
/**
|
||||
* Utility wrapper around promises to help control flow in tests
|
||||
* Calls `fn` function `tries` times, with a sleep of `interval` between calls.
|
||||
* Ensure you do not rely on any effects of calling any `cy.*` functions within the body of `fn`
|
||||
* as the calls will not happen until after waitForPromise returns.
|
||||
* @param fn the function to retry
|
||||
* @param tries the number of tries to call it
|
||||
* @param interval the time interval between tries
|
||||
*/
|
||||
waitForPromise(fn: () => Promise<unknown>, tries?: number, interval?: number): Chainable<unknown>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function waitForPromise(fn: () => Promise<unknown>, tries = 10, interval = 1000): Chainable<unknown> {
|
||||
return cy.then(
|
||||
() =>
|
||||
new Cypress.Promise(async (resolve, reject) => {
|
||||
for (let i = 0; i < tries; i++) {
|
||||
try {
|
||||
const v = await fn();
|
||||
resolve(v);
|
||||
} catch {
|
||||
await new Cypress.Promise((resolve) => setTimeout(resolve, interval));
|
||||
}
|
||||
}
|
||||
reject();
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Cypress.Commands.add("waitForPromise", waitForPromise);
|
||||
|
||||
export {};
|
|
@ -208,6 +208,7 @@
|
|||
"jest-mock": "^29.2.2",
|
||||
"jest-raw-loader": "^1.0.1",
|
||||
"jsqr": "^1.4.0",
|
||||
"mailhog": "^4.16.0",
|
||||
"matrix-mock-request": "^2.5.0",
|
||||
"matrix-web-i18n": "^1.4.0",
|
||||
"mocha-junit-reporter": "^2.2.0",
|
||||
|
|
|
@ -176,8 +176,6 @@ interface IProps {
|
|||
initialScreenAfterLogin?: IScreen;
|
||||
// displayname, if any, to set on the device when logging in/registering.
|
||||
defaultDeviceDisplayName?: string;
|
||||
// A function that makes a registration URL
|
||||
makeRegistrationUrl: (params: QueryDict) => string;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
|
@ -2004,13 +2002,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
this.setState({ serverConfig });
|
||||
};
|
||||
|
||||
private makeRegistrationUrl = (params: QueryDict): string => {
|
||||
if (this.props.startingFragmentQueryParams?.referrer) {
|
||||
params.referrer = this.props.startingFragmentQueryParams.referrer;
|
||||
}
|
||||
return this.props.makeRegistrationUrl(params);
|
||||
};
|
||||
|
||||
/**
|
||||
* After registration or login, we run various post-auth steps before entering the app
|
||||
* proper, such setting up cross-signing or verifying the new session.
|
||||
|
@ -2121,7 +2112,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
idSid={this.state.register_id_sid}
|
||||
email={email}
|
||||
brand={this.props.config.brand}
|
||||
makeRegistrationUrl={this.makeRegistrationUrl}
|
||||
onLoggedIn={this.onRegisterFlowComplete}
|
||||
onLoginClick={this.onLoginClick}
|
||||
onServerConfigChange={this.onServerConfigChange}
|
||||
|
|
|
@ -73,15 +73,7 @@ interface IProps {
|
|||
// - The user's password, if available and applicable (may be cached in memory
|
||||
// for a short time so the user is not required to re-enter their password
|
||||
// for operations like uploading cross-signing keys).
|
||||
onLoggedIn(params: IMatrixClientCreds, password: string): void;
|
||||
makeRegistrationUrl(params: {
|
||||
/* eslint-disable camelcase */
|
||||
client_secret: string;
|
||||
hs_url: string;
|
||||
is_url?: string;
|
||||
session_id: string;
|
||||
/* eslint-enable camelcase */
|
||||
}): string;
|
||||
onLoggedIn(params: IMatrixClientCreds, password: string): Promise<void>;
|
||||
// registration shouldn't know or care how login is done.
|
||||
onLoginClick(): void;
|
||||
onServerConfigChange(config: ValidatedServerConfig): void;
|
||||
|
@ -302,17 +294,7 @@ export default class Registration extends React.Component<IProps, IState> {
|
|||
sessionId: string,
|
||||
): Promise<IRequestTokenResponse> => {
|
||||
if (!this.state.matrixClient) throw new Error("Matrix client has not yet been loaded");
|
||||
return this.state.matrixClient.requestRegisterEmailToken(
|
||||
emailAddress,
|
||||
clientSecret,
|
||||
sendAttempt,
|
||||
this.props.makeRegistrationUrl({
|
||||
client_secret: clientSecret,
|
||||
hs_url: this.state.matrixClient.getHomeserverUrl(),
|
||||
is_url: this.state.matrixClient.getIdentityServerUrl(),
|
||||
session_id: sessionId,
|
||||
}),
|
||||
);
|
||||
return this.state.matrixClient.requestRegisterEmailToken(emailAddress, clientSecret, sendAttempt);
|
||||
};
|
||||
|
||||
private onUIAuthFinished: InteractiveAuthCallback<RegisterResponse> = async (success, response): Promise<void> => {
|
||||
|
@ -401,9 +383,7 @@ export default class Registration extends React.Component<IProps, IState> {
|
|||
const hasAccessToken = Boolean(accessToken);
|
||||
debuglog("Registration: ui auth finished:", { hasEmail, hasAccessToken });
|
||||
// don’t log in if we found a session for a different user
|
||||
if (!hasEmail && hasAccessToken && !newState.differentLoggedInUserId) {
|
||||
// we'll only try logging in if we either have no email to verify at all or we're the client that verified
|
||||
// the email, not the client that started the registration flow
|
||||
if (hasAccessToken && !newState.differentLoggedInUserId) {
|
||||
await this.props.onLoggedIn(
|
||||
{
|
||||
userId,
|
||||
|
|
|
@ -131,7 +131,6 @@ describe("<MatrixChat />", () => {
|
|||
},
|
||||
onNewScreen: jest.fn(),
|
||||
onTokenLoginCompleted: jest.fn(),
|
||||
makeRegistrationUrl: jest.fn(),
|
||||
realQueryParams: {},
|
||||
};
|
||||
const getComponent = (props: Partial<ComponentProps<typeof MatrixChat>> = {}) =>
|
||||
|
|
|
@ -75,7 +75,6 @@ describe("Registration", function () {
|
|||
|
||||
const defaultProps = {
|
||||
defaultDeviceDisplayName: "test-device-display-name",
|
||||
makeRegistrationUrl: jest.fn(),
|
||||
onLoggedIn: jest.fn(),
|
||||
onLoginClick: jest.fn(),
|
||||
onServerConfigChange: jest.fn(),
|
||||
|
|
49
yarn.lock
49
yarn.lock
|
@ -1389,9 +1389,9 @@
|
|||
integrity sha512-hBI9tfBtuPIi885ZsZ32IMEU/5nlZH/KOVYJCOh7gyMxaVLGmLedYqFN6Ui1LXkI8JlC8IsuC0rF0btcRZKd5g==
|
||||
|
||||
"@cypress/request@^2.88.11":
|
||||
version "2.88.11"
|
||||
resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.11.tgz#5a4c7399bc2d7e7ed56e92ce5acb620c8b187047"
|
||||
integrity sha512-M83/wfQ1EkspjkE2lNWNV5ui2Cv7UCv1swW1DqljahbzLVWltcsexQh8jYtuS/vzFXP+HySntGM83ZXA9fn17w==
|
||||
version "2.88.12"
|
||||
resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.12.tgz#ba4911431738494a85e93fb04498cb38bc55d590"
|
||||
integrity sha512-tOn+0mDZxASFM+cuAP9szGUGPI1HwWVSvdzm7V4cCsPdFTx6qMj29CwaQmRAMIEhORIUBFBsYROYJcveK4uOjA==
|
||||
dependencies:
|
||||
aws-sign2 "~0.7.0"
|
||||
aws4 "^1.8.0"
|
||||
|
@ -1408,7 +1408,7 @@
|
|||
performance-now "^2.1.0"
|
||||
qs "~6.10.3"
|
||||
safe-buffer "^5.1.2"
|
||||
tough-cookie "~2.5.0"
|
||||
tough-cookie "^4.1.3"
|
||||
tunnel-agent "^0.6.0"
|
||||
uuid "^8.3.2"
|
||||
|
||||
|
@ -2615,16 +2615,16 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.8.tgz#b5dda19adaa473a9bf0ab5cbd8f30ec7d43f5c85"
|
||||
integrity sha512-0mHckf6D2DiIAzh8fM8f3HQCvMKDpK94YQ0DSVkfWTG9BZleYIWudw9cJxX8oCk9bM+vAkDyujDV6dmKHbvQpg==
|
||||
|
||||
"@types/node@^14.14.31":
|
||||
version "14.18.54"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.54.tgz#fc304bd66419030141fa997dc5a9e0e374029ae8"
|
||||
integrity sha512-uq7O52wvo2Lggsx1x21tKZgqkJpvwCseBBPtX/nKQfpVlEsLOb11zZ1CRsWUKvJF0+lzuA9jwvA7Pr2Wt7i3xw==
|
||||
|
||||
"@types/node@^16":
|
||||
version "16.18.39"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.39.tgz#aa39a1a87a40ef6098ee69689a1acb0c1b034832"
|
||||
integrity sha512-8q9ZexmdYYyc5/cfujaXb4YOucpQxAV4RMG0himLyDUOEr8Mr79VrqsFI+cQ2M2h89YIuy95lbxuYjxT4Hk4kQ==
|
||||
|
||||
"@types/node@^16.18.39":
|
||||
version "16.18.40"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.40.tgz#968d64746d20cac747a18ca982c0f1fe518c031c"
|
||||
integrity sha512-+yno3ItTEwGxXiS/75Q/aHaa5srkpnJaH+kdkTVJ3DtJEwv92itpKbxU+FjPoh2m/5G9zmUQfrL4A4C13c+iGA==
|
||||
|
||||
"@types/normalize-package-data@^2.4.0":
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301"
|
||||
|
@ -4039,13 +4039,13 @@ cypress-terminal-report@^5.3.2:
|
|||
tv4 "^1.3.0"
|
||||
|
||||
cypress@^12.0.0:
|
||||
version "12.17.2"
|
||||
resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.17.2.tgz#040ac55de1e811f6e037d231a2869d5ab8c29c85"
|
||||
integrity sha512-hxWAaWbqQBzzMuadSGSuQg5PDvIGOovm6xm0hIfpCVcORsCAj/gF2p0EvfnJ4f+jK2PCiDgP6D2eeE9/FK4Mjg==
|
||||
version "12.17.3"
|
||||
resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.17.3.tgz#1e2b19037236fc60e4a4b3a14f0846be17a1fc0e"
|
||||
integrity sha512-/R4+xdIDjUSLYkiQfwJd630S81KIgicmQOLXotFxVXkl+eTeVO+3bHXxdi5KBh/OgC33HWN33kHX+0tQR/ZWpg==
|
||||
dependencies:
|
||||
"@cypress/request" "^2.88.11"
|
||||
"@cypress/xvfb" "^1.2.4"
|
||||
"@types/node" "^14.14.31"
|
||||
"@types/node" "^16.18.39"
|
||||
"@types/sinonjs__fake-timers" "8.1.1"
|
||||
"@types/sizzle" "^2.3.2"
|
||||
arch "^2.2.0"
|
||||
|
@ -5775,7 +5775,7 @@ iconv-lite@0.4.24:
|
|||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3"
|
||||
|
||||
iconv-lite@0.6.3:
|
||||
iconv-lite@0.6.3, iconv-lite@^0.6:
|
||||
version "0.6.3"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
|
||||
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
|
||||
|
@ -7078,6 +7078,13 @@ lz-string@^1.4.4:
|
|||
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941"
|
||||
integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==
|
||||
|
||||
mailhog@^4.16.0:
|
||||
version "4.16.0"
|
||||
resolved "https://registry.yarnpkg.com/mailhog/-/mailhog-4.16.0.tgz#1ad4dda104505399f3f17824737a962696e7d240"
|
||||
integrity sha512-wXrGik+0MaAy4dbYTImxa8niX9a4aRpZTzC/b1GzCvQs09khhs0aKZgHjgScakI4Y18WInDvvF48hhEz9ifN4g==
|
||||
optionalDependencies:
|
||||
iconv-lite "^0.6"
|
||||
|
||||
make-dir@^2.0.0, make-dir@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
|
||||
|
@ -8051,7 +8058,7 @@ proxy-from-env@1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee"
|
||||
integrity sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==
|
||||
|
||||
psl@^1.1.28, psl@^1.1.33:
|
||||
psl@^1.1.33:
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"
|
||||
integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==
|
||||
|
@ -9269,13 +9276,15 @@ tough-cookie@^4.1.2:
|
|||
universalify "^0.2.0"
|
||||
url-parse "^1.5.3"
|
||||
|
||||
tough-cookie@~2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
|
||||
integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
|
||||
tough-cookie@^4.1.3:
|
||||
version "4.1.3"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf"
|
||||
integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==
|
||||
dependencies:
|
||||
psl "^1.1.28"
|
||||
psl "^1.1.33"
|
||||
punycode "^2.1.1"
|
||||
universalify "^0.2.0"
|
||||
url-parse "^1.5.3"
|
||||
|
||||
tr46@^1.0.1:
|
||||
version "1.0.1"
|
||||
|
|
Loading…
Reference in New Issue