mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-15 12:45:11 +08:00
Chat export parameter customisation (#7647)
* use export settings and hide fields Signed-off-by: Kerry Archibald <kerrya@element.io> * fix exporter tests Signed-off-by: Kerry Archibald <kerrya@element.io> * test ExportDialog with settings Signed-off-by: Kerry Archibald <kerrya@element.io> * tidy debugs, rename setting to Parameters Signed-off-by: Kerry Archibald <kerrya@element.io> * use reasonable 100gb limit Signed-off-by: Kerry Archibald <kerrya@element.io> * use normal setting instead of UIFeature Signed-off-by: Kerry Archibald <kerrya@element.io> * use a customisation Signed-off-by: Kerry Archibald <kerrya@element.io> * move validateNumberInRange to utils Signed-off-by: Kerry Archibald <kerrya@element.io> * use nullish coalesce Signed-off-by: Kerry Archibald <kerrya@element.io> * use 8gb size limit for customisation Signed-off-by: Kerry Archibald <kerrya@element.io> * update comments Signed-off-by: Kerry Archibald <kerrya@element.io>
This commit is contained in:
parent
ad87ee0a0f
commit
085ecc7f5f
@ -191,6 +191,7 @@
|
||||
"stylelint": "^13.9.0",
|
||||
"stylelint-config-standard": "^20.0.0",
|
||||
"stylelint-scss": "^3.18.0",
|
||||
"ts-jest": "^27.1.3",
|
||||
"typescript": "4.5.3",
|
||||
"walk": "^2.3.14"
|
||||
},
|
||||
|
@ -89,3 +89,7 @@ limitations under the License.
|
||||
padding: 9px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_ExportDialog_attachments-checkbox {
|
||||
margin-top: $spacing-16;
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useRef, useState } from "react";
|
||||
import React, { useRef, useState, Dispatch, SetStateAction } from "react";
|
||||
import { Room } from "matrix-js-sdk/src";
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
@ -39,18 +39,70 @@ import { useStateCallback } from "../../../hooks/useStateCallback";
|
||||
import Exporter from "../../../utils/exportUtils/Exporter";
|
||||
import Spinner from "../elements/Spinner";
|
||||
import InfoDialog from "./InfoDialog";
|
||||
import ChatExport from "../../../customisations/ChatExport";
|
||||
import { validateNumberInRange } from "../../../utils/validate";
|
||||
|
||||
interface IProps extends IDialogProps {
|
||||
room: Room;
|
||||
}
|
||||
|
||||
interface ExportConfig {
|
||||
exportFormat: ExportFormat;
|
||||
exportType: ExportType;
|
||||
numberOfMessages: number;
|
||||
sizeLimit: number;
|
||||
includeAttachments: boolean;
|
||||
setExportFormat?: Dispatch<SetStateAction<ExportFormat>>;
|
||||
setExportType?: Dispatch<SetStateAction<ExportType>>;
|
||||
setAttachments?: Dispatch<SetStateAction<boolean>>;
|
||||
setNumberOfMessages?: Dispatch<SetStateAction<number>>;
|
||||
setSizeLimit?: Dispatch<SetStateAction<number>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up form state using "forceRoomExportParameters" or defaults
|
||||
* Form fields configured in ForceRoomExportParameters are not allowed to be edited
|
||||
* Only return change handlers for editable values
|
||||
*/
|
||||
const useExportFormState = (): ExportConfig => {
|
||||
const config = ChatExport.getForceChatExportParameters();
|
||||
|
||||
const [exportFormat, setExportFormat] = useState(config.format ?? ExportFormat.Html);
|
||||
const [exportType, setExportType] = useState(config.range ?? ExportType.Timeline);
|
||||
const [includeAttachments, setAttachments] =
|
||||
useState(config.includeAttachments ?? false);
|
||||
const [numberOfMessages, setNumberOfMessages] = useState<number>(config.numberOfMessages ?? 100);
|
||||
const [sizeLimit, setSizeLimit] = useState<number | null>(config.sizeMb ?? 8);
|
||||
|
||||
return {
|
||||
exportFormat,
|
||||
exportType,
|
||||
includeAttachments,
|
||||
numberOfMessages,
|
||||
sizeLimit,
|
||||
setExportFormat: !config.format ? setExportFormat : undefined,
|
||||
setExportType: !config.range ? setExportType : undefined,
|
||||
setNumberOfMessages: !config.numberOfMessages ? setNumberOfMessages : undefined,
|
||||
setSizeLimit: !config.sizeMb ? setSizeLimit : undefined,
|
||||
setAttachments: config.includeAttachments === undefined ? setAttachments : undefined,
|
||||
};
|
||||
};
|
||||
|
||||
const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
|
||||
const [exportFormat, setExportFormat] = useState(ExportFormat.Html);
|
||||
const [exportType, setExportType] = useState(ExportType.Timeline);
|
||||
const [includeAttachments, setAttachments] = useState(false);
|
||||
const {
|
||||
exportFormat,
|
||||
exportType,
|
||||
includeAttachments,
|
||||
numberOfMessages,
|
||||
sizeLimit,
|
||||
setExportFormat,
|
||||
setExportType,
|
||||
setNumberOfMessages,
|
||||
setSizeLimit,
|
||||
setAttachments,
|
||||
} = useExportFormState();
|
||||
|
||||
const [isExporting, setExporting] = useState(false);
|
||||
const [numberOfMessages, setNumberOfMessages] = useState<number>(100);
|
||||
const [sizeLimit, setSizeLimit] = useState<number | null>(8);
|
||||
const sizeLimitRef = useRef<Field>();
|
||||
const messageCountRef = useRef<Field>();
|
||||
const [exportProgressText, setExportProgressText] = useState(_t("Processing..."));
|
||||
@ -110,9 +162,10 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
|
||||
};
|
||||
|
||||
const onExportClick = async () => {
|
||||
const isValidSize = await sizeLimitRef.current.validate({
|
||||
const isValidSize = !setSizeLimit || (await sizeLimitRef.current.validate({
|
||||
focused: false,
|
||||
});
|
||||
}));
|
||||
|
||||
if (!isValidSize) {
|
||||
sizeLimitRef.current.validate({ focused: true });
|
||||
return;
|
||||
@ -147,10 +200,8 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
|
||||
}, {
|
||||
key: "number",
|
||||
test: ({ value }) => {
|
||||
const parsedSize = parseFloat(value);
|
||||
const min = 1;
|
||||
const max = 2000;
|
||||
return !(isNaN(parsedSize) || min > parsedSize || parsedSize > max);
|
||||
const parsedSize = parseInt(value as string, 10);
|
||||
return validateNumberInRange(1, 2000)(parsedSize);
|
||||
},
|
||||
invalid: () => {
|
||||
const min = 1;
|
||||
@ -187,11 +238,8 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
|
||||
}, {
|
||||
key: "number",
|
||||
test: ({ value }) => {
|
||||
const parsedSize = parseFloat(value);
|
||||
const min = 1;
|
||||
const max = 10 ** 8;
|
||||
if (isNaN(parsedSize)) return false;
|
||||
return !(min > parsedSize || parsedSize > max);
|
||||
const parsedSize = parseInt(value as string, 10);
|
||||
return validateNumberInRange(1, 10 ** 8)(parsedSize);
|
||||
},
|
||||
invalid: () => {
|
||||
const min = 1;
|
||||
@ -236,7 +284,7 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
|
||||
});
|
||||
|
||||
let messageCount = null;
|
||||
if (exportType === ExportType.LastNMessages) {
|
||||
if (exportType === ExportType.LastNMessages && setNumberOfMessages) {
|
||||
messageCount = (
|
||||
<Field
|
||||
id="message-count"
|
||||
@ -319,61 +367,74 @@ const ExportDialog: React.FC<IProps> = ({ room, onFinished }) => {
|
||||
) }
|
||||
</p> : null }
|
||||
|
||||
<span className="mx_ExportDialog_subheading">
|
||||
{ _t("Format") }
|
||||
</span>
|
||||
|
||||
<div className="mx_ExportDialog_options">
|
||||
<StyledRadioGroup
|
||||
name="exportFormat"
|
||||
value={exportFormat}
|
||||
onChange={(key) => setExportFormat(ExportFormat[key])}
|
||||
definitions={exportFormatOptions}
|
||||
/>
|
||||
{ !!setExportFormat && <>
|
||||
<span className="mx_ExportDialog_subheading">
|
||||
{ _t("Format") }
|
||||
</span>
|
||||
|
||||
<span className="mx_ExportDialog_subheading">
|
||||
{ _t("Messages") }
|
||||
</span>
|
||||
<StyledRadioGroup
|
||||
name="exportFormat"
|
||||
value={exportFormat}
|
||||
onChange={(key) => setExportFormat(ExportFormat[key])}
|
||||
definitions={exportFormatOptions}
|
||||
/>
|
||||
</> }
|
||||
|
||||
<Field
|
||||
id="export-type"
|
||||
element="select"
|
||||
value={exportType}
|
||||
onChange={(e) => {
|
||||
setExportType(ExportType[e.target.value]);
|
||||
}}
|
||||
>
|
||||
{ exportTypeOptions }
|
||||
</Field>
|
||||
{ messageCount }
|
||||
{
|
||||
!!setExportType && <>
|
||||
|
||||
<span className="mx_ExportDialog_subheading">
|
||||
{ _t("Size Limit") }
|
||||
</span>
|
||||
<span className="mx_ExportDialog_subheading">
|
||||
{ _t("Messages") }
|
||||
</span>
|
||||
|
||||
<Field
|
||||
id="size-limit"
|
||||
type="number"
|
||||
autoComplete="off"
|
||||
onValidate={onValidateSize}
|
||||
element="input"
|
||||
ref={sizeLimitRef}
|
||||
value={sizeLimit.toString()}
|
||||
postfixComponent={sizePostFix}
|
||||
onChange={(e) => setSizeLimit(parseInt(e.target.value))}
|
||||
/>
|
||||
<Field
|
||||
id="export-type"
|
||||
element="select"
|
||||
value={exportType}
|
||||
onChange={(e) => {
|
||||
setExportType(ExportType[e.target.value]);
|
||||
}}
|
||||
>
|
||||
{ exportTypeOptions }
|
||||
</Field>
|
||||
{ messageCount }
|
||||
</>
|
||||
}
|
||||
|
||||
{ setSizeLimit && <>
|
||||
<span className="mx_ExportDialog_subheading">
|
||||
{ _t("Size Limit") }
|
||||
</span>
|
||||
|
||||
<Field
|
||||
id="size-limit"
|
||||
type="number"
|
||||
autoComplete="off"
|
||||
onValidate={onValidateSize}
|
||||
element="input"
|
||||
ref={sizeLimitRef}
|
||||
value={sizeLimit.toString()}
|
||||
postfixComponent={sizePostFix}
|
||||
onChange={(e) => setSizeLimit(parseInt(e.target.value))}
|
||||
/>
|
||||
</> }
|
||||
|
||||
{ setAttachments && <>
|
||||
<StyledCheckbox
|
||||
className="mx_ExportDialog_attachments-checkbox"
|
||||
id="include-attachments"
|
||||
checked={includeAttachments}
|
||||
onChange={(e) =>
|
||||
setAttachments(
|
||||
(e.target as HTMLInputElement).checked,
|
||||
)
|
||||
}
|
||||
>
|
||||
{ _t("Include Attachments") }
|
||||
</StyledCheckbox>
|
||||
</> }
|
||||
|
||||
<StyledCheckbox
|
||||
id="include-attachments"
|
||||
checked={includeAttachments}
|
||||
onChange={(e) =>
|
||||
setAttachments(
|
||||
(e.target as HTMLInputElement).checked,
|
||||
)
|
||||
}
|
||||
>
|
||||
{ _t("Include Attachments") }
|
||||
</StyledCheckbox>
|
||||
</div>
|
||||
{ isExporting ? (
|
||||
<div data-test-id='export-progress' className="mx_ExportDialog_progress">
|
||||
|
52
src/customisations/ChatExport.ts
Normal file
52
src/customisations/ChatExport.ts
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
Copyright 2022 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.
|
||||
*/
|
||||
|
||||
import { ExportFormat, ExportType } from "../utils/exportUtils/exportUtils";
|
||||
|
||||
export type ForceChatExportParameters = {
|
||||
format?: ExportFormat;
|
||||
range?: ExportType;
|
||||
// must be < 10**8
|
||||
// only used when range is 'LastNMessages'
|
||||
// default is 100
|
||||
numberOfMessages?: number;
|
||||
includeAttachments?: boolean;
|
||||
// maximum size of exported archive
|
||||
// must be > 0 and < 8000
|
||||
sizeMb?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Force parameters in room chat export
|
||||
* fields returned here are forced
|
||||
* and not allowed to be edited in the chat export form
|
||||
*/
|
||||
const getForceChatExportParameters = (): ForceChatExportParameters => {
|
||||
return {};
|
||||
};
|
||||
|
||||
// This interface summarises all available customisation points and also marks
|
||||
// them all as optional. This allows customisers to only define and export the
|
||||
// customisations they need while still maintaining type safety.
|
||||
export interface IChatExportCustomisations {
|
||||
getForceChatExportParameters?: typeof getForceChatExportParameters;
|
||||
}
|
||||
|
||||
// A real customisation module will define and export one or more of the
|
||||
// customisation points that make up `IChatExportCustomisations`.
|
||||
export default {
|
||||
getForceChatExportParameters,
|
||||
} as IChatExportCustomisations;
|
@ -32,7 +32,7 @@ export enum UIFeature {
|
||||
Communities = "UIFeature.communities",
|
||||
AdvancedSettings = "UIFeature.advancedSettings",
|
||||
RoomHistorySettings = "UIFeature.roomHistorySettings",
|
||||
TimelineEnableRelativeDates = "UIFeature.timelineEnableRelativeDates"
|
||||
TimelineEnableRelativeDates = "UIFeature.timelineEnableRelativeDates",
|
||||
}
|
||||
|
||||
export enum UIComponent {
|
||||
|
@ -48,7 +48,7 @@ export default abstract class Exporter {
|
||||
protected setProgressText: React.Dispatch<React.SetStateAction<string>>,
|
||||
) {
|
||||
if (exportOptions.maxSize < 1 * 1024 * 1024|| // Less than 1 MB
|
||||
exportOptions.maxSize > 2000 * 1024 * 1024|| // More than ~ 2 GB
|
||||
exportOptions.maxSize > 8000 * 1024 * 1024 || // More than 8 GB
|
||||
exportOptions.numberOfMessages > 10**8
|
||||
) {
|
||||
throw new Error("Invalid export options");
|
||||
|
1
src/utils/validate/index.ts
Normal file
1
src/utils/validate/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./numberInRange";
|
9
src/utils/validate/numberInRange.ts
Normal file
9
src/utils/validate/numberInRange.ts
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
/**
|
||||
* Validates that a value is
|
||||
* - a number
|
||||
* - in a provided range (inclusive)
|
||||
*/
|
||||
export const validateNumberInRange = (min: number, max: number) => (value?: number) => {
|
||||
return typeof value === 'number' && !(isNaN(value) || min > value || value > max);
|
||||
};
|
@ -16,6 +16,7 @@ limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import { mocked } from 'ts-jest/utils';
|
||||
import '../../../skinned-sdk';
|
||||
import { act } from "react-dom/test-utils";
|
||||
import { Room } from 'matrix-js-sdk';
|
||||
@ -25,13 +26,27 @@ import { ExportType, ExportFormat } from '../../../../src/utils/exportUtils/expo
|
||||
import { createTestClient, mkStubRoom } from '../../../test-utils';
|
||||
import { MatrixClientPeg } from '../../../../src/MatrixClientPeg';
|
||||
import HTMLExporter from "../../../../src/utils/exportUtils/HtmlExport";
|
||||
import ChatExport from '../../../../src/customisations/ChatExport';
|
||||
import PlainTextExporter from '../../../../src/utils/exportUtils/PlainTextExport';
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
const mockHtmlExporter = ({
|
||||
const htmlExporterInstance = ({
|
||||
export: jest.fn().mockResolvedValue({}),
|
||||
});
|
||||
const plainTextExporterInstance = ({
|
||||
export: jest.fn().mockResolvedValue({}),
|
||||
});
|
||||
jest.mock("../../../../src/utils/exportUtils/HtmlExport", () => jest.fn());
|
||||
jest.mock("../../../../src/utils/exportUtils/PlainTextExport", () => jest.fn());
|
||||
|
||||
jest.mock('../../../../src/customisations/ChatExport', () => ({
|
||||
getForceChatExportParameters: jest.fn().mockReturnValue({}),
|
||||
}));
|
||||
|
||||
const ChatExportMock = mocked(ChatExport);
|
||||
const HTMLExporterMock = mocked(HTMLExporter);
|
||||
const PlainTextExporterMock = mocked(PlainTextExporter);
|
||||
|
||||
describe('<ExportDialog />', () => {
|
||||
const mockClient = createTestClient();
|
||||
@ -81,8 +96,13 @@ describe('<ExportDialog />', () => {
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
(HTMLExporter as jest.Mock).mockImplementation(jest.fn().mockReturnValue(mockHtmlExporter));
|
||||
mockHtmlExporter.export.mockClear();
|
||||
HTMLExporterMock.mockClear().mockImplementation(jest.fn().mockReturnValue(htmlExporterInstance));
|
||||
PlainTextExporterMock.mockClear().mockImplementation(jest.fn().mockReturnValue(plainTextExporterInstance));
|
||||
htmlExporterInstance.export.mockClear();
|
||||
plainTextExporterInstance.export.mockClear();
|
||||
|
||||
// default setting value
|
||||
ChatExportMock.getForceChatExportParameters.mockClear().mockReturnValue({});
|
||||
});
|
||||
|
||||
it('renders export dialog', () => {
|
||||
@ -104,7 +124,7 @@ describe('<ExportDialog />', () => {
|
||||
await submitForm(component);
|
||||
|
||||
// 4th arg is an component function
|
||||
const exportConstructorProps = (HTMLExporter as jest.Mock).mock.calls[0].slice(0, 3);
|
||||
const exportConstructorProps = HTMLExporterMock.mock.calls[0].slice(0, 3);
|
||||
expect(exportConstructorProps).toEqual([
|
||||
defaultProps.room,
|
||||
ExportType.Timeline,
|
||||
@ -114,7 +134,32 @@ describe('<ExportDialog />', () => {
|
||||
numberOfMessages: 100,
|
||||
},
|
||||
]);
|
||||
expect(mockHtmlExporter.export).toHaveBeenCalled();
|
||||
expect(htmlExporterInstance.export).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('exports room using values set from ForceRoomExportParameters', async () => {
|
||||
ChatExportMock.getForceChatExportParameters.mockReturnValue({
|
||||
format: ExportFormat.PlainText,
|
||||
range: ExportType.Beginning,
|
||||
sizeMb: 7000,
|
||||
numberOfMessages: 30,
|
||||
includeAttachments: true,
|
||||
});
|
||||
const component = getComponent();
|
||||
await submitForm(component);
|
||||
|
||||
// 4th arg is an component function
|
||||
const exportConstructorProps = PlainTextExporterMock.mock.calls[0].slice(0, 3);
|
||||
expect(exportConstructorProps).toEqual([
|
||||
defaultProps.room,
|
||||
ExportType.Beginning,
|
||||
{
|
||||
attachmentsIncluded: true,
|
||||
maxSize: 7000 * 1024 * 1024,
|
||||
numberOfMessages: 30,
|
||||
},
|
||||
]);
|
||||
expect(plainTextExporterInstance.export).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('renders success screen when export is finished', async () => {
|
||||
@ -139,6 +184,19 @@ describe('<ExportDialog />', () => {
|
||||
expect(getExportFormatInput(component, ExportFormat.PlainText).props().checked).toBeTruthy();
|
||||
expect(getExportFormatInput(component, ExportFormat.Html).props().checked).toBeFalsy();
|
||||
});
|
||||
|
||||
it('hides export format input when format is valid in ForceRoomExportParameters', () => {
|
||||
const component = getComponent();
|
||||
expect(getExportFormatInput(component, ExportFormat.Html).props().checked).toBeTruthy();
|
||||
});
|
||||
|
||||
it('does not render export format when set in ForceRoomExportParameters', () => {
|
||||
ChatExportMock.getForceChatExportParameters.mockReturnValue({
|
||||
format: ExportFormat.PlainText,
|
||||
});
|
||||
const component = getComponent();
|
||||
expect(getExportFormatInput(component, ExportFormat.Html).length).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('export type', () => {
|
||||
@ -153,6 +211,14 @@ describe('<ExportDialog />', () => {
|
||||
expect(getExportTypeInput(component).props().value).toEqual(ExportType.Beginning);
|
||||
});
|
||||
|
||||
it('does not render export type when set in ForceRoomExportParameters', () => {
|
||||
ChatExportMock.getForceChatExportParameters.mockReturnValue({
|
||||
range: ExportType.Beginning,
|
||||
});
|
||||
const component = getComponent();
|
||||
expect(getExportTypeInput(component).length).toBeFalsy();
|
||||
});
|
||||
|
||||
it('does not render message count input', async () => {
|
||||
const component = getComponent();
|
||||
expect(getMessageCountInput(component).length).toBeFalsy();
|
||||
@ -177,7 +243,7 @@ describe('<ExportDialog />', () => {
|
||||
await setMessageCount(component, 0);
|
||||
await submitForm(component);
|
||||
|
||||
expect(mockHtmlExporter.export).not.toHaveBeenCalled();
|
||||
expect(htmlExporterInstance.export).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not export when export type is lastNMessages and message count is more than max', async () => {
|
||||
@ -186,7 +252,7 @@ describe('<ExportDialog />', () => {
|
||||
await setMessageCount(component, 99999999999);
|
||||
await submitForm(component);
|
||||
|
||||
expect(mockHtmlExporter.export).not.toHaveBeenCalled();
|
||||
expect(htmlExporterInstance.export).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('exports when export type is NOT lastNMessages and message count is falsy', async () => {
|
||||
@ -196,7 +262,7 @@ describe('<ExportDialog />', () => {
|
||||
await selectExportType(component, ExportType.Timeline);
|
||||
await submitForm(component);
|
||||
|
||||
expect(mockHtmlExporter.export).toHaveBeenCalled();
|
||||
expect(htmlExporterInstance.export).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@ -217,7 +283,7 @@ describe('<ExportDialog />', () => {
|
||||
await setSizeLimit(component, 0);
|
||||
await submitForm(component);
|
||||
|
||||
expect(mockHtmlExporter.export).not.toHaveBeenCalled();
|
||||
expect(htmlExporterInstance.export).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not export when size limit is larger than max', async () => {
|
||||
@ -225,7 +291,7 @@ describe('<ExportDialog />', () => {
|
||||
await setSizeLimit(component, 2001);
|
||||
await submitForm(component);
|
||||
|
||||
expect(mockHtmlExporter.export).not.toHaveBeenCalled();
|
||||
expect(htmlExporterInstance.export).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('exports when size limit is max', async () => {
|
||||
@ -233,11 +299,32 @@ describe('<ExportDialog />', () => {
|
||||
await setSizeLimit(component, 2000);
|
||||
await submitForm(component);
|
||||
|
||||
expect(mockHtmlExporter.export).toHaveBeenCalled();
|
||||
expect(htmlExporterInstance.export).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does not render size limit input when set in ForceRoomExportParameters', () => {
|
||||
ChatExportMock.getForceChatExportParameters.mockReturnValue({
|
||||
sizeMb: 10000,
|
||||
});
|
||||
const component = getComponent();
|
||||
expect(getSizeInput(component).length).toBeFalsy();
|
||||
});
|
||||
|
||||
/**
|
||||
* 2000mb size limit does not apply when higher limit is configured in config
|
||||
*/
|
||||
it('exports when size limit set in ForceRoomExportParameters is larger than 2000', async () => {
|
||||
ChatExportMock.getForceChatExportParameters.mockReturnValue({
|
||||
sizeMb: 10000,
|
||||
});
|
||||
const component = getComponent();
|
||||
await submitForm(component);
|
||||
|
||||
expect(htmlExporterInstance.export).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('include attachements', () => {
|
||||
describe('include attachments', () => {
|
||||
it('renders input with default value of false', () => {
|
||||
const component = getComponent();
|
||||
expect(getAttachmentsCheckbox(component).props().checked).toEqual(false);
|
||||
@ -248,6 +335,14 @@ describe('<ExportDialog />', () => {
|
||||
await setIncludeAttachments(component, true);
|
||||
expect(getAttachmentsCheckbox(component).props().checked).toEqual(true);
|
||||
});
|
||||
|
||||
it('does not render input when set in ForceRoomExportParameters', () => {
|
||||
ChatExportMock.getForceChatExportParameters.mockReturnValue({
|
||||
includeAttachments: false,
|
||||
});
|
||||
const component = getComponent();
|
||||
expect(getAttachmentsCheckbox(component).length).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -105,14 +105,14 @@ Array [
|
||||
<p>
|
||||
Select from the options below to export chats from your timeline
|
||||
</p>
|
||||
<span
|
||||
class="mx_ExportDialog_subheading"
|
||||
>
|
||||
Format
|
||||
</span>
|
||||
<div
|
||||
class="mx_ExportDialog_options"
|
||||
>
|
||||
<span
|
||||
class="mx_ExportDialog_subheading"
|
||||
>
|
||||
Format
|
||||
</span>
|
||||
<label
|
||||
class="mx_StyledRadioButton mx_StyledRadioButton_enabled mx_StyledRadioButton_checked"
|
||||
>
|
||||
@ -235,7 +235,7 @@ Array [
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
class="mx_Checkbox mx_ExportDialog_attachments-checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
>
|
||||
<input
|
||||
id="include-attachments"
|
||||
@ -326,14 +326,14 @@ Array [
|
||||
<p>
|
||||
Select from the options below to export chats from your timeline
|
||||
</p>
|
||||
<span
|
||||
class="mx_ExportDialog_subheading"
|
||||
>
|
||||
Format
|
||||
</span>
|
||||
<div
|
||||
class="mx_ExportDialog_options"
|
||||
>
|
||||
<span
|
||||
class="mx_ExportDialog_subheading"
|
||||
>
|
||||
Format
|
||||
</span>
|
||||
<label
|
||||
class="mx_StyledRadioButton mx_StyledRadioButton_enabled mx_StyledRadioButton_checked"
|
||||
>
|
||||
@ -456,7 +456,7 @@ Array [
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
class="mx_Checkbox mx_ExportDialog_attachments-checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
>
|
||||
<input
|
||||
id="include-attachments"
|
||||
@ -563,14 +563,14 @@ Array [
|
||||
<p>
|
||||
Select from the options below to export chats from your timeline
|
||||
</p>
|
||||
<span
|
||||
className="mx_ExportDialog_subheading"
|
||||
>
|
||||
Format
|
||||
</span>
|
||||
<div
|
||||
className="mx_ExportDialog_options"
|
||||
>
|
||||
<span
|
||||
className="mx_ExportDialog_subheading"
|
||||
>
|
||||
Format
|
||||
</span>
|
||||
<StyledRadioGroup
|
||||
definitions={
|
||||
Array [
|
||||
@ -790,12 +790,12 @@ Array [
|
||||
</Field>
|
||||
<StyledCheckbox
|
||||
checked={false}
|
||||
className=""
|
||||
className="mx_ExportDialog_attachments-checkbox"
|
||||
id="include-attachments"
|
||||
onChange={[Function]}
|
||||
>
|
||||
<span
|
||||
className="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
className="mx_Checkbox mx_ExportDialog_attachments-checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
>
|
||||
<input
|
||||
checked={false}
|
||||
@ -962,14 +962,14 @@ Array [
|
||||
<p>
|
||||
Select from the options below to export chats from your timeline
|
||||
</p>
|
||||
<span
|
||||
class="mx_ExportDialog_subheading"
|
||||
>
|
||||
Format
|
||||
</span>
|
||||
<div
|
||||
class="mx_ExportDialog_options"
|
||||
>
|
||||
<span
|
||||
class="mx_ExportDialog_subheading"
|
||||
>
|
||||
Format
|
||||
</span>
|
||||
<label
|
||||
class="mx_StyledRadioButton mx_StyledRadioButton_enabled mx_StyledRadioButton_checked"
|
||||
>
|
||||
@ -1092,7 +1092,7 @@ Array [
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
class="mx_Checkbox mx_ExportDialog_attachments-checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
>
|
||||
<input
|
||||
id="include-attachments"
|
||||
@ -1183,14 +1183,14 @@ Array [
|
||||
<p>
|
||||
Select from the options below to export chats from your timeline
|
||||
</p>
|
||||
<span
|
||||
class="mx_ExportDialog_subheading"
|
||||
>
|
||||
Format
|
||||
</span>
|
||||
<div
|
||||
class="mx_ExportDialog_options"
|
||||
>
|
||||
<span
|
||||
class="mx_ExportDialog_subheading"
|
||||
>
|
||||
Format
|
||||
</span>
|
||||
<label
|
||||
class="mx_StyledRadioButton mx_StyledRadioButton_enabled mx_StyledRadioButton_checked"
|
||||
>
|
||||
@ -1313,7 +1313,7 @@ Array [
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
class="mx_Checkbox mx_ExportDialog_attachments-checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
>
|
||||
<input
|
||||
id="include-attachments"
|
||||
@ -1420,14 +1420,14 @@ Array [
|
||||
<p>
|
||||
Select from the options below to export chats from your timeline
|
||||
</p>
|
||||
<span
|
||||
className="mx_ExportDialog_subheading"
|
||||
>
|
||||
Format
|
||||
</span>
|
||||
<div
|
||||
className="mx_ExportDialog_options"
|
||||
>
|
||||
<span
|
||||
className="mx_ExportDialog_subheading"
|
||||
>
|
||||
Format
|
||||
</span>
|
||||
<StyledRadioGroup
|
||||
definitions={
|
||||
Array [
|
||||
@ -1647,12 +1647,12 @@ Array [
|
||||
</Field>
|
||||
<StyledCheckbox
|
||||
checked={false}
|
||||
className=""
|
||||
className="mx_ExportDialog_attachments-checkbox"
|
||||
id="include-attachments"
|
||||
onChange={[Function]}
|
||||
>
|
||||
<span
|
||||
className="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
className="mx_Checkbox mx_ExportDialog_attachments-checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
>
|
||||
<input
|
||||
checked={false}
|
||||
@ -1806,14 +1806,14 @@ Array [
|
||||
<p>
|
||||
Select from the options below to export chats from your timeline
|
||||
</p>
|
||||
<span
|
||||
class="mx_ExportDialog_subheading"
|
||||
>
|
||||
Format
|
||||
</span>
|
||||
<div
|
||||
class="mx_ExportDialog_options"
|
||||
>
|
||||
<span
|
||||
class="mx_ExportDialog_subheading"
|
||||
>
|
||||
Format
|
||||
</span>
|
||||
<label
|
||||
class="mx_StyledRadioButton mx_StyledRadioButton_enabled mx_StyledRadioButton_checked"
|
||||
>
|
||||
@ -1936,7 +1936,7 @@ Array [
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
class="mx_Checkbox mx_ExportDialog_attachments-checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
>
|
||||
<input
|
||||
id="include-attachments"
|
||||
@ -2027,14 +2027,14 @@ Array [
|
||||
<p>
|
||||
Select from the options below to export chats from your timeline
|
||||
</p>
|
||||
<span
|
||||
class="mx_ExportDialog_subheading"
|
||||
>
|
||||
Format
|
||||
</span>
|
||||
<div
|
||||
class="mx_ExportDialog_options"
|
||||
>
|
||||
<span
|
||||
class="mx_ExportDialog_subheading"
|
||||
>
|
||||
Format
|
||||
</span>
|
||||
<label
|
||||
class="mx_StyledRadioButton mx_StyledRadioButton_enabled mx_StyledRadioButton_checked"
|
||||
>
|
||||
@ -2157,7 +2157,7 @@ Array [
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
class="mx_Checkbox mx_ExportDialog_attachments-checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
>
|
||||
<input
|
||||
id="include-attachments"
|
||||
@ -2264,14 +2264,14 @@ Array [
|
||||
<p>
|
||||
Select from the options below to export chats from your timeline
|
||||
</p>
|
||||
<span
|
||||
className="mx_ExportDialog_subheading"
|
||||
>
|
||||
Format
|
||||
</span>
|
||||
<div
|
||||
className="mx_ExportDialog_options"
|
||||
>
|
||||
<span
|
||||
className="mx_ExportDialog_subheading"
|
||||
>
|
||||
Format
|
||||
</span>
|
||||
<StyledRadioGroup
|
||||
definitions={
|
||||
Array [
|
||||
@ -2491,12 +2491,12 @@ Array [
|
||||
</Field>
|
||||
<StyledCheckbox
|
||||
checked={false}
|
||||
className=""
|
||||
className="mx_ExportDialog_attachments-checkbox"
|
||||
id="include-attachments"
|
||||
onChange={[Function]}
|
||||
>
|
||||
<span
|
||||
className="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
className="mx_Checkbox mx_ExportDialog_attachments-checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
>
|
||||
<input
|
||||
checked={false}
|
||||
@ -2613,14 +2613,14 @@ Array [
|
||||
<p>
|
||||
Select from the options below to export chats from your timeline
|
||||
</p>
|
||||
<span
|
||||
className="mx_ExportDialog_subheading"
|
||||
>
|
||||
Format
|
||||
</span>
|
||||
<div
|
||||
className="mx_ExportDialog_options"
|
||||
>
|
||||
<span
|
||||
className="mx_ExportDialog_subheading"
|
||||
>
|
||||
Format
|
||||
</span>
|
||||
<StyledRadioGroup
|
||||
definitions={
|
||||
Array [
|
||||
@ -2840,12 +2840,12 @@ Array [
|
||||
</Field>
|
||||
<StyledCheckbox
|
||||
checked={false}
|
||||
className=""
|
||||
className="mx_ExportDialog_attachments-checkbox"
|
||||
id="include-attachments"
|
||||
onChange={[Function]}
|
||||
>
|
||||
<span
|
||||
className="mx_Checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
className="mx_Checkbox mx_ExportDialog_attachments-checkbox mx_Checkbox_hasKind mx_Checkbox_kind_solid"
|
||||
>
|
||||
<input
|
||||
checked={false}
|
||||
|
@ -50,24 +50,6 @@ describe('export', function() {
|
||||
attachmentsIncluded: false,
|
||||
};
|
||||
|
||||
const invalidExportOptions: IExportOptions[] = [
|
||||
{
|
||||
numberOfMessages: 10**9,
|
||||
maxSize: 1024 * 1024 * 1024,
|
||||
attachmentsIncluded: false,
|
||||
},
|
||||
{
|
||||
numberOfMessages: -1,
|
||||
maxSize: 4096 * 1024 * 1024,
|
||||
attachmentsIncluded: false,
|
||||
},
|
||||
{
|
||||
numberOfMessages: 0,
|
||||
maxSize: 0,
|
||||
attachmentsIncluded: false,
|
||||
},
|
||||
];
|
||||
|
||||
function createRoom() {
|
||||
const room = new Room(generateRoomId(), null, client.getUserId());
|
||||
return room;
|
||||
@ -212,13 +194,28 @@ describe('export', function() {
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it('checks if the export options are valid', function() {
|
||||
for (const exportOption of invalidExportOptions) {
|
||||
expect(
|
||||
() =>
|
||||
new PlainTextExporter(mockRoom, ExportType.Beginning, exportOption, null),
|
||||
).toThrowError("Invalid export options");
|
||||
}
|
||||
const invalidExportOptions: [string, IExportOptions][] = [
|
||||
['numberOfMessages exceeds max', {
|
||||
numberOfMessages: 10 ** 9,
|
||||
maxSize: 1024 * 1024 * 1024,
|
||||
attachmentsIncluded: false,
|
||||
}],
|
||||
['maxSize exceeds 8GB', {
|
||||
numberOfMessages: -1,
|
||||
maxSize: 8001 * 1024 * 1024,
|
||||
attachmentsIncluded: false,
|
||||
}],
|
||||
['maxSize is less than 1mb', {
|
||||
numberOfMessages: 0,
|
||||
maxSize: 0,
|
||||
attachmentsIncluded: false,
|
||||
}],
|
||||
];
|
||||
it.each(invalidExportOptions)('%s', (_d, options) => {
|
||||
expect(
|
||||
() =>
|
||||
new PlainTextExporter(mockRoom, ExportType.Beginning, options, null),
|
||||
).toThrowError("Invalid export options");
|
||||
});
|
||||
|
||||
it('tests the file extension splitter', function() {
|
||||
|
26
test/utils/validate/numberInRange-test.ts
Normal file
26
test/utils/validate/numberInRange-test.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { validateNumberInRange } from '../../../src/utils/validate';
|
||||
|
||||
describe('validateNumberInRange', () => {
|
||||
const min = 1; const max = 10;
|
||||
it('returns false when value is a not a number', () => {
|
||||
expect(validateNumberInRange(min, max)('test' as unknown as number)).toEqual(false);
|
||||
});
|
||||
it('returns false when value is undefined', () => {
|
||||
expect(validateNumberInRange(min, max)(undefined)).toEqual(false);
|
||||
});
|
||||
it('returns false when value is NaN', () => {
|
||||
expect(validateNumberInRange(min, max)(NaN)).toEqual(false);
|
||||
});
|
||||
it('returns true when value is equal to min', () => {
|
||||
expect(validateNumberInRange(min, max)(min)).toEqual(true);
|
||||
});
|
||||
it('returns true when value is equal to max', () => {
|
||||
expect(validateNumberInRange(min, max)(max)).toEqual(true);
|
||||
});
|
||||
it('returns true when value is an int in range', () => {
|
||||
expect(validateNumberInRange(min, max)(2)).toEqual(true);
|
||||
});
|
||||
it('returns true when value is a float in range', () => {
|
||||
expect(validateNumberInRange(min, max)(2.2)).toEqual(true);
|
||||
});
|
||||
});
|
104
yarn.lock
104
yarn.lock
@ -1334,6 +1334,17 @@
|
||||
"@types/yargs" "^15.0.0"
|
||||
chalk "^4.0.0"
|
||||
|
||||
"@jest/types@^27.4.2":
|
||||
version "27.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.4.2.tgz#96536ebd34da6392c2b7c7737d693885b5dd44a5"
|
||||
integrity sha512-j35yw0PMTPpZsUoOBiuHzr1zTYoad1cVIE0ajEjcrJONxxrko/IRGKkXx3os0Nsi4Hu3+5VmDbVfq5WhG/pWAg==
|
||||
dependencies:
|
||||
"@types/istanbul-lib-coverage" "^2.0.0"
|
||||
"@types/istanbul-reports" "^3.0.0"
|
||||
"@types/node" "*"
|
||||
"@types/yargs" "^16.0.0"
|
||||
chalk "^4.0.0"
|
||||
|
||||
"@mapbox/geojson-rewind@^0.5.0":
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@mapbox/geojson-rewind/-/geojson-rewind-0.5.1.tgz#adbe16dc683eb40e90934c51a5e28c7bbf44f4e1"
|
||||
@ -1983,6 +1994,13 @@
|
||||
dependencies:
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
"@types/yargs@^16.0.0":
|
||||
version "16.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977"
|
||||
integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==
|
||||
dependencies:
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
"@types/zxcvbn@^4.4.0":
|
||||
version "4.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/zxcvbn/-/zxcvbn-4.4.1.tgz#46e42cbdcee681b22181478feaf4af2bc4c1abd2"
|
||||
@ -2634,6 +2652,13 @@ browserslist@^4.12.0, browserslist@^4.17.5, browserslist@^4.19.1:
|
||||
node-releases "^2.0.1"
|
||||
picocolors "^1.0.0"
|
||||
|
||||
bs-logger@0.x:
|
||||
version "0.2.6"
|
||||
resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8"
|
||||
integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==
|
||||
dependencies:
|
||||
fast-json-stable-stringify "2.x"
|
||||
|
||||
bs58@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a"
|
||||
@ -2832,6 +2857,11 @@ ci-info@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
|
||||
integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
|
||||
|
||||
ci-info@^3.2.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2"
|
||||
integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==
|
||||
|
||||
cjs-module-lexer@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-0.6.0.tgz#4186fcca0eae175970aee870b9fe2d6cf8d5655f"
|
||||
@ -4079,7 +4109,7 @@ fast-glob@^3.2.5, fast-glob@^3.2.9:
|
||||
merge2 "^1.3.0"
|
||||
micromatch "^4.0.4"
|
||||
|
||||
fast-json-stable-stringify@^2.0.0:
|
||||
fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
|
||||
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
|
||||
@ -5697,6 +5727,18 @@ jest-util@^26.6.2:
|
||||
is-ci "^2.0.0"
|
||||
micromatch "^4.0.2"
|
||||
|
||||
jest-util@^27.0.0:
|
||||
version "27.4.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.4.2.tgz#ed95b05b1adfd761e2cda47e0144c6a58e05a621"
|
||||
integrity sha512-YuxxpXU6nlMan9qyLuxHaMMOzXAl5aGZWCSzben5DhLHemYQxCc4YK+4L3ZrCutT8GPQ+ui9k5D8rUJoDioMnA==
|
||||
dependencies:
|
||||
"@jest/types" "^27.4.2"
|
||||
"@types/node" "*"
|
||||
chalk "^4.0.0"
|
||||
ci-info "^3.2.0"
|
||||
graceful-fs "^4.2.4"
|
||||
picomatch "^2.2.3"
|
||||
|
||||
jest-validate@^26.6.2:
|
||||
version "26.6.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.6.2.tgz#23d380971587150467342911c3d7b4ac57ab20ec"
|
||||
@ -5843,6 +5885,13 @@ json-stringify-safe@~5.0.1:
|
||||
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
|
||||
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
|
||||
|
||||
json5@2.x, json5@^2.1.2:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3"
|
||||
integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==
|
||||
dependencies:
|
||||
minimist "^1.2.5"
|
||||
|
||||
json5@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
|
||||
@ -5850,13 +5899,6 @@ json5@^1.0.1:
|
||||
dependencies:
|
||||
minimist "^1.2.0"
|
||||
|
||||
json5@^2.1.2:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3"
|
||||
integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==
|
||||
dependencies:
|
||||
minimist "^1.2.5"
|
||||
|
||||
jsprim@^1.2.2:
|
||||
version "1.4.2"
|
||||
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb"
|
||||
@ -6028,6 +6070,11 @@ lodash.isequal@^4.5.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
||||
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
|
||||
|
||||
lodash.memoize@4.x:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
|
||||
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
|
||||
|
||||
lodash.truncate@^4.4.2:
|
||||
version "4.4.2"
|
||||
resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
|
||||
@ -6099,6 +6146,11 @@ make-dir@^3.0.0:
|
||||
dependencies:
|
||||
semver "^6.0.0"
|
||||
|
||||
make-error@1.x:
|
||||
version "1.3.6"
|
||||
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
|
||||
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
|
||||
|
||||
makeerror@1.0.12:
|
||||
version "1.0.12"
|
||||
resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a"
|
||||
@ -7787,18 +7839,18 @@ semver@7.0.0:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
|
||||
integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
|
||||
|
||||
semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||
|
||||
semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5:
|
||||
semver@7.x, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5:
|
||||
version "7.3.5"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
|
||||
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||
|
||||
set-blocking@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
|
||||
@ -8484,6 +8536,20 @@ trough@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406"
|
||||
integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==
|
||||
|
||||
ts-jest@^27.1.3:
|
||||
version "27.1.3"
|
||||
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.1.3.tgz#1f723e7e74027c4da92c0ffbd73287e8af2b2957"
|
||||
integrity sha512-6Nlura7s6uM9BVUAoqLH7JHyMXjz8gluryjpPXxr3IxZdAXnU6FhjvVLHFtfd1vsE1p8zD1OJfskkc0jhTSnkA==
|
||||
dependencies:
|
||||
bs-logger "0.x"
|
||||
fast-json-stable-stringify "2.x"
|
||||
jest-util "^27.0.0"
|
||||
json5 "2.x"
|
||||
lodash.memoize "4.x"
|
||||
make-error "1.x"
|
||||
semver "7.x"
|
||||
yargs-parser "20.x"
|
||||
|
||||
tsconfig-paths@^3.12.0:
|
||||
version "3.12.0"
|
||||
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz#19769aca6ee8f6a1a341e38c8fa45dd9fb18899b"
|
||||
@ -9057,6 +9123,11 @@ yaml@^1.10.0:
|
||||
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
|
||||
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
|
||||
|
||||
yargs-parser@20.x, yargs-parser@^20.2.3:
|
||||
version "20.2.9"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
|
||||
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
|
||||
|
||||
yargs-parser@^13.1.2:
|
||||
version "13.1.2"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38"
|
||||
@ -9073,11 +9144,6 @@ yargs-parser@^18.1.2:
|
||||
camelcase "^5.0.0"
|
||||
decamelize "^1.2.0"
|
||||
|
||||
yargs-parser@^20.2.3:
|
||||
version "20.2.9"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
|
||||
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
|
||||
|
||||
yargs-parser@^21.0.0:
|
||||
version "21.0.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.0.tgz#a485d3966be4317426dd56bdb6a30131b281dc55"
|
||||
|
Loading…
Reference in New Issue
Block a user