mirror of
https://github.com/haveno-dex/haveno-ui.git
synced 2024-10-01 07:35:39 -04:00
chore(dev): code sweep for tests and storybook
chore: updated README chore: added button tests and stories fix: removed unused code from @atoms/Currency chore: added tests and stories for @atoms/Link test: refactored NodeConnectSwitch refactored chore: refactored NodeStatus test: atom/ProtectedRoute feat: added PasswordInput atom chore: refactored account sidebar test: PaymentMethodCard and AddPaymentMethod button test: ReadyToUse molecule test: SetPassword organism test: Login page chore: added stories for onboarding pages --- Reviewed-by: localredhead
This commit is contained in:
parent
7ada7f17db
commit
8eb4694ca2
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,6 +3,7 @@ node_modules
|
||||
dist
|
||||
*.local
|
||||
thumbs.db
|
||||
yarn-error.log
|
||||
|
||||
.eslintcache
|
||||
|
||||
|
@ -3,5 +3,8 @@ node_modules
|
||||
buildResources
|
||||
packages/main/dist
|
||||
packages/preload/dist
|
||||
packages/rendered/dist
|
||||
packages/renderer/dist
|
||||
packages/main/coverage
|
||||
packages/preload/coverage
|
||||
packages/renderer/coverage
|
||||
dist/
|
||||
|
@ -19,10 +19,7 @@ const svgrPlugin = require("vite-plugin-svgr");
|
||||
const viteConfig = require("../packages/renderer/vite.config");
|
||||
|
||||
module.exports = {
|
||||
stories: [
|
||||
"../packages/renderer/**/*.stories.mdx",
|
||||
"../packages/renderer/**/*.stories.@(js|jsx|ts|tsx)",
|
||||
],
|
||||
stories: ["../packages/renderer/src/**/*.stories.tsx"],
|
||||
addons: [
|
||||
"@storybook/addon-links",
|
||||
"@storybook/addon-essentials",
|
||||
|
38
README.md
38
README.md
@ -1,3 +1,37 @@
|
||||
# haveno-ui
|
||||
# Haveno User Interface
|
||||
|
||||
Haveno user interface
|
||||
## Development
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. Node 16.x
|
||||
1. yarn 1.x
|
||||
1. Haveno daemon and envoy proxy
|
||||
|
||||
### Install dependencies
|
||||
|
||||
```sh
|
||||
yarn
|
||||
```
|
||||
|
||||
### Configure environment variables
|
||||
|
||||
Copy [.env.example](./.env.example) to a file called `.env` and point the environment variables to the envoy proxy.
|
||||
|
||||
### Start the app in watch mode
|
||||
|
||||
```sh
|
||||
yarn watch
|
||||
```
|
||||
|
||||
### Tests
|
||||
|
||||
```sh
|
||||
yarn tests
|
||||
```
|
||||
|
||||
### Storybook
|
||||
|
||||
```sh
|
||||
yarn storybook
|
||||
```
|
||||
|
@ -46,10 +46,12 @@
|
||||
"@storybook/addon-essentials": "^6.4.22",
|
||||
"@storybook/addon-interactions": "^6.4.22",
|
||||
"@storybook/addon-links": "^6.4.22",
|
||||
"@storybook/builder-vite": "^0.1.29",
|
||||
"@storybook/builder-vite": "^0.1.34",
|
||||
"@storybook/react": "^6.4.22",
|
||||
"@storybook/testing-library": "^0.0.10",
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react": "^12",
|
||||
"@testing-library/user-event": "^14.2.0",
|
||||
"@types/jsonwebtoken": "^8.5.8",
|
||||
"@types/lodash": "^4.14.182",
|
||||
"@types/react": "<18.0.0",
|
||||
@ -69,6 +71,7 @@
|
||||
"eslint-plugin-react": "^7.29.4",
|
||||
"happy-dom": "2.41.0",
|
||||
"husky": "^7.0.4",
|
||||
"jsdom": "^19.0.0",
|
||||
"nano-staged": "^0.7.0",
|
||||
"playwright": "1.19.1",
|
||||
"prettier": "^2.6.2",
|
||||
@ -97,6 +100,7 @@
|
||||
"react-intl": "^5.24.8",
|
||||
"react-query": "^3.34.19",
|
||||
"react-router-dom": "6",
|
||||
"recoil": "^0.7.0"
|
||||
"recoil": "^0.7.0",
|
||||
"tabler-icons-react": "^1.48.0"
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
import { Group, Stack } from "@mantine/core";
|
||||
import type { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||
import { Button } from ".";
|
||||
import { Button, TextButton } from ".";
|
||||
|
||||
export default {
|
||||
title: "atoms/Buttons",
|
||||
@ -30,12 +30,14 @@ const Template: ComponentStory<typeof Button> = () => {
|
||||
<Button flavor="neutral">Neutral</Button>
|
||||
<Button flavor="success">Success</Button>
|
||||
<Button flavor="danger">Error</Button>
|
||||
<TextButton>Click me</TextButton>
|
||||
|
||||
<Group>
|
||||
<Button flavor="primary">Primary</Button>
|
||||
<Button flavor="neutral">Neutral</Button>
|
||||
<Button flavor="success">Success</Button>
|
||||
<Button flavor="danger">Error</Button>
|
||||
<TextButton>Click me</TextButton>
|
||||
</Group>
|
||||
</Stack>
|
||||
);
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { render } from "@testing-library/react";
|
||||
import { Button } from ".";
|
||||
import { Button, TextButton } from ".";
|
||||
|
||||
describe("atoms::Buttons", () => {
|
||||
it("renders primary button by default", () => {
|
||||
@ -38,4 +38,11 @@ describe("atoms::Buttons", () => {
|
||||
const { asFragment } = render(<Button flavor="danger">Error</Button>);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders text button", () => {
|
||||
const { asFragment } = render(
|
||||
<TextButton>This is a Text Button</TextButton>
|
||||
);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
@ -26,7 +26,7 @@ interface TextButtonProps extends UnstyledButtonProps<"button"> {
|
||||
export function TextButton(props: TextButtonProps) {
|
||||
const { children, ...rest } = props;
|
||||
return (
|
||||
<UnstyledButton {...rest}>
|
||||
<UnstyledButton {...rest} sx={{ textAlign: "center" }}>
|
||||
<BodyText component="span" heavy sx={{ textDecoration: "underline" }}>
|
||||
{children}
|
||||
</BodyText>
|
||||
|
@ -75,3 +75,18 @@ exports[`atoms::Buttons > renders success button 1`] = `
|
||||
</button>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`atoms::Buttons > renders text button 1`] = `
|
||||
<DocumentFragment>
|
||||
<button
|
||||
class="mantine-UnstyledButton-root mantine-13uxwbm"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="mantine-Text-root mantine-10a0kig"
|
||||
>
|
||||
This is a Text Button
|
||||
</span>
|
||||
</button>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
@ -28,8 +28,7 @@ export function Currency(props: CurrencyProps) {
|
||||
|
||||
const formattedNumber = useMemo(
|
||||
() =>
|
||||
intl
|
||||
.formatNumber(value, {
|
||||
intl.formatNumber(value, {
|
||||
...(currencyCode
|
||||
? {
|
||||
currency: currencyCode,
|
||||
@ -41,8 +40,7 @@ export function Currency(props: CurrencyProps) {
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 12,
|
||||
}),
|
||||
})
|
||||
.replace(/XXX\s/, ""),
|
||||
}),
|
||||
[currencyCode, value]
|
||||
);
|
||||
|
||||
|
@ -14,27 +14,26 @@
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { SecondarySidebarItem } from "@molecules/SecondarySidebar";
|
||||
import { useNavLinkActive } from "@src/hooks/misc/useNavLinkActive";
|
||||
import { Stack } from "@mantine/core";
|
||||
import type { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||
import type { LinkProps } from ".";
|
||||
import { Link } from ".";
|
||||
|
||||
interface AccountSidebarItemProps {
|
||||
label: string;
|
||||
route: string;
|
||||
}
|
||||
|
||||
export function AccountSidebarItem({ label, route }: AccountSidebarItemProps) {
|
||||
const isActive = useNavLinkActive({ to: route });
|
||||
const navigate = useNavigate();
|
||||
export default {
|
||||
title: "atoms/Link",
|
||||
component: Link,
|
||||
} as ComponentMeta<typeof Link>;
|
||||
|
||||
const Template: ComponentStory<typeof Link> = ({ children, to }: LinkProps) => {
|
||||
return (
|
||||
<SecondarySidebarItem
|
||||
key={label}
|
||||
label={label}
|
||||
isActive={isActive}
|
||||
onClick={() => {
|
||||
return navigate(route);
|
||||
}}
|
||||
/>
|
||||
<Stack>
|
||||
<Link to={to}>{children}</Link>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
children: "Click me",
|
||||
to: "/",
|
||||
};
|
31
packages/renderer/src/components/atoms/Link/Link.test.tsx
Normal file
31
packages/renderer/src/components/atoms/Link/Link.test.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// 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 { describe, expect, it } from "vitest";
|
||||
import { render } from "@testing-library/react";
|
||||
import { AppProviders } from "@atoms/AppProviders";
|
||||
import { Link } from ".";
|
||||
|
||||
describe("atoms::Link", () => {
|
||||
it("renders without exploding", () => {
|
||||
const { asFragment } = render(
|
||||
<AppProviders>
|
||||
<Link to="/">Click me</Link>
|
||||
</AppProviders>
|
||||
);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
});
|
@ -19,7 +19,7 @@ import { Link as RouterLink } from "react-router-dom";
|
||||
import { BodyText } from "@atoms/Typography";
|
||||
import type { ReactText } from "react";
|
||||
|
||||
interface LinkProps extends RouterLinkProps {
|
||||
export interface LinkProps extends RouterLinkProps {
|
||||
children: ReactText;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,15 @@
|
||||
// Vitest Snapshot v1
|
||||
|
||||
exports[`atoms::Link > renders without exploding 1`] = `
|
||||
<DocumentFragment>
|
||||
<a
|
||||
href="#/"
|
||||
>
|
||||
<span
|
||||
class="mantine-Text-root mantine-ix3vgq"
|
||||
>
|
||||
Click me
|
||||
</span>
|
||||
</a>
|
||||
</DocumentFragment>
|
||||
`;
|
@ -14,12 +14,13 @@
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import { Box, createStyles, Text } from "@mantine/core";
|
||||
import { createStyles, Box, Text } from "@mantine/core";
|
||||
|
||||
export enum NodeStatusType {
|
||||
Active = "active",
|
||||
Inactive = "inactive",
|
||||
}
|
||||
|
||||
export interface NodeStatusProps {
|
||||
/** Node title */
|
||||
title: string;
|
||||
@ -34,9 +35,9 @@ export function NodeStatus({ title, status }: NodeStatusProps) {
|
||||
return (
|
||||
<Box className={classes.root}>
|
||||
<Text className={classes.title}>{title}</Text>
|
||||
<div className={classes.status}>
|
||||
<div className={classes.statusInner} />
|
||||
</div>
|
||||
<Box className={classes.status}>
|
||||
<Box className={classes.statusInner} />
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@ -49,7 +50,7 @@ export const useStyles = createStyles<string, { status: NodeStatusType }>(
|
||||
theme.colorScheme === "dark" ? theme.colors.dark[8] : theme.white,
|
||||
border: `1px solid ${theme.colors.gray[2]}`,
|
||||
borderRadius: theme.radius.md,
|
||||
padding: "0.91rem",
|
||||
padding: "0.875rem",
|
||||
display: "flex",
|
||||
transition: "background-color 0.1s ease-in-out",
|
||||
},
|
||||
|
@ -3,7 +3,7 @@
|
||||
exports[`atoms::NodeStatus > renders without exploding 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mantine-18xx7au"
|
||||
class="mantine-1vax8o0"
|
||||
>
|
||||
<div
|
||||
class="mantine-Text-root mantine-14byb36"
|
||||
@ -11,15 +11,15 @@ exports[`atoms::NodeStatus > renders without exploding 1`] = `
|
||||
node.moneroworldcom:18089:active
|
||||
</div>
|
||||
<div
|
||||
class="mantine-17do188"
|
||||
class="mantine-1d0mff5"
|
||||
>
|
||||
<div
|
||||
class="mantine-167633s"
|
||||
class="mantine-zy87za"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mantine-18xx7au"
|
||||
class="mantine-1vax8o0"
|
||||
>
|
||||
<div
|
||||
class="mantine-Text-root mantine-14byb36"
|
||||
@ -27,10 +27,10 @@ exports[`atoms::NodeStatus > renders without exploding 1`] = `
|
||||
node.moneroworldcom:18089:inactive
|
||||
</div>
|
||||
<div
|
||||
class="mantine-17do188"
|
||||
class="mantine-1d0mff5"
|
||||
>
|
||||
<div
|
||||
class="mantine-1cipvbv"
|
||||
class="mantine-19v6ci5"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -0,0 +1,43 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// 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 { Stack } from "@mantine/core";
|
||||
import type { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||
import { EyeCheck, EyeOff } from "tabler-icons-react";
|
||||
import { PasswordInput } from ".";
|
||||
|
||||
export default {
|
||||
title: "atoms/PasswordInput",
|
||||
component: PasswordInput,
|
||||
} as ComponentMeta<typeof PasswordInput>;
|
||||
|
||||
const Template: ComponentStory<typeof PasswordInput> = () => {
|
||||
return (
|
||||
<Stack>
|
||||
<PasswordInput
|
||||
id="password"
|
||||
label="Change visibility toggle icon"
|
||||
placeholder="Change visibility toggle icon"
|
||||
defaultValue="secret"
|
||||
visibilityToggleIcon={({ reveal, size }) =>
|
||||
reveal ? <EyeOff size={size} /> : <EyeCheck size={size} />
|
||||
}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
@ -0,0 +1,28 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// 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 { describe, expect, it } from "vitest";
|
||||
import { render } from "@testing-library/react";
|
||||
import { PasswordInput } from ".";
|
||||
|
||||
describe("atoms::PasswordInput", () => {
|
||||
it("renders without exploding", () => {
|
||||
const { asFragment } = render(
|
||||
<PasswordInput id="pass" label="Enter password" />
|
||||
);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
});
|
@ -0,0 +1,45 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// 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 type { PasswordInputProps as MPasswordInputProps } from "@mantine/core";
|
||||
import { createStyles, PasswordInput as MPasswordInput } from "@mantine/core";
|
||||
|
||||
interface PasswordInputProps extends MPasswordInputProps {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export function PasswordInput(props: PasswordInputProps) {
|
||||
const { id, ...rest } = props;
|
||||
const { classes } = useStyles();
|
||||
return <MPasswordInput classNames={classes} id={id} {...rest} />;
|
||||
}
|
||||
|
||||
const useStyles = createStyles((theme) => ({
|
||||
label: {
|
||||
fontSize: "0.875rem",
|
||||
fontWeight: 600,
|
||||
marginBottom: theme.spacing.sm,
|
||||
},
|
||||
innerInput: {
|
||||
fontSize: "0.875rem",
|
||||
fontWeight: 700,
|
||||
height: "3rem",
|
||||
padding: "1rem",
|
||||
},
|
||||
input: {
|
||||
height: "3rem",
|
||||
},
|
||||
}));
|
@ -0,0 +1,56 @@
|
||||
// Vitest Snapshot v1
|
||||
|
||||
exports[`atoms::PasswordInput > renders without exploding 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mantine-PasswordInput-root mantine-18udhi"
|
||||
>
|
||||
<label
|
||||
class="mantine-PasswordInput-label mantine-7802ha"
|
||||
for="pass"
|
||||
id="pass-label"
|
||||
>
|
||||
Enter password
|
||||
</label>
|
||||
<div
|
||||
class="mantine-PasswordInput-wrapper mantine-12sbrde"
|
||||
>
|
||||
<div
|
||||
aria-invalid="false"
|
||||
class="mantine-PasswordInput-defaultVariant mantine-PasswordInput-input mantine-PasswordInput-input mantine-1i2duzg"
|
||||
>
|
||||
<input
|
||||
class="mantine-PasswordInput-innerInput mantine-1bj8gkk"
|
||||
id="pass"
|
||||
type="password"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="mantine-o3oqoy mantine-PasswordInput-rightSection"
|
||||
>
|
||||
<button
|
||||
aria-hidden="true"
|
||||
class="mantine-ActionIcon-hover mantine-ActionIcon-root mantine-PasswordInput-visibilityToggle mantine-vao037"
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
fill="none"
|
||||
height="15"
|
||||
viewBox="0 0 15 15"
|
||||
width="15"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M7.5 11C4.80285 11 2.52952 9.62184 1.09622 7.50001C2.52952 5.37816 4.80285 4 7.5 4C10.1971 4 12.4705 5.37816 13.9038 7.50001C12.4705 9.62183 10.1971 11 7.5 11ZM7.5 3C4.30786 3 1.65639 4.70638 0.0760002 7.23501C-0.0253338 7.39715 -0.0253334 7.60288 0.0760014 7.76501C1.65639 10.2936 4.30786 12 7.5 12C10.6921 12 13.3436 10.2936 14.924 7.76501C15.0253 7.60288 15.0253 7.39715 14.924 7.23501C13.3436 4.70638 10.6921 3 7.5 3ZM7.5 9.5C8.60457 9.5 9.5 8.60457 9.5 7.5C9.5 6.39543 8.60457 5.5 7.5 5.5C6.39543 5.5 5.5 6.39543 5.5 7.5C5.5 8.60457 6.39543 9.5 7.5 9.5Z"
|
||||
fill="currentColor"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
@ -14,4 +14,4 @@
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
export * from "./Wallet";
|
||||
export * from "./PasswordInput";
|
@ -0,0 +1,105 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// 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.
|
||||
// =============================================================================
|
||||
|
||||
const navigateSpy = vi.fn();
|
||||
const deleteSessionSpy = vi.fn();
|
||||
|
||||
import type { SpyInstanceFn } from "vitest";
|
||||
import { beforeAll, describe, expect, it, vi, afterEach } from "vitest";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { useAuth } from "@hooks/session/useAuth";
|
||||
import { ProtectedRoute } from "./ProtectedRoute";
|
||||
import { ROUTES } from "@constants/routes";
|
||||
|
||||
describe("atoms::ProtectedRoute", () => {
|
||||
beforeAll(() => {
|
||||
vi.mock("react-router-dom", () => ({
|
||||
useNavigate: () => navigateSpy,
|
||||
}));
|
||||
|
||||
vi.mock("@utils/session", () => ({
|
||||
deleteSession: deleteSessionSpy,
|
||||
}));
|
||||
|
||||
vi.mock("@hooks/session/useAuth", () => ({
|
||||
useAuth: vi.fn(() => ({
|
||||
isLoading: true,
|
||||
isSuccess: false,
|
||||
data: undefined,
|
||||
})),
|
||||
}));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it("renders children if auth session exists", () => {
|
||||
(useAuth as SpyInstanceFn).mockReturnValue({
|
||||
isLoading: false,
|
||||
isSuccess: true,
|
||||
data: true,
|
||||
});
|
||||
const { asFragment, unmount } = render(
|
||||
<ProtectedRoute>
|
||||
<div>Protected content</div>
|
||||
</ProtectedRoute>
|
||||
);
|
||||
expect(screen.queryByText("Protected content")).toBeInTheDocument();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it("skips rendering children and redirects to login if no auth session exists", () => {
|
||||
(useAuth as SpyInstanceFn).mockReturnValueOnce({
|
||||
isLoading: false,
|
||||
isSuccess: true,
|
||||
data: false,
|
||||
});
|
||||
|
||||
const { asFragment, unmount } = render(
|
||||
<ProtectedRoute>
|
||||
<div>Protected content</div>
|
||||
</ProtectedRoute>
|
||||
);
|
||||
expect(screen.queryByText("Protected content")).toBeNull();
|
||||
expect(deleteSessionSpy).toHaveBeenCalledOnce();
|
||||
expect(navigateSpy).toHaveBeenCalledOnce();
|
||||
expect(navigateSpy).toHaveBeenCalledWith(ROUTES.Login);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it("skips rendering children and redirects to login if auth session can't be retrieved", () => {
|
||||
(useAuth as SpyInstanceFn).mockReturnValueOnce({
|
||||
isLoading: false,
|
||||
isSuccess: false,
|
||||
data: true,
|
||||
});
|
||||
|
||||
const { asFragment, unmount } = render(
|
||||
<ProtectedRoute>
|
||||
<div>Protected content</div>
|
||||
</ProtectedRoute>
|
||||
);
|
||||
expect(screen.queryByText("Protected content")).toBeNull();
|
||||
expect(deleteSessionSpy).toHaveBeenCalledOnce();
|
||||
expect(navigateSpy).toHaveBeenCalledOnce();
|
||||
expect(navigateSpy).toHaveBeenCalledWith(ROUTES.Login);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
unmount();
|
||||
});
|
||||
});
|
@ -18,7 +18,7 @@ import type { ReactNode } from "react";
|
||||
import { useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useAuth } from "@hooks/session/useAuth";
|
||||
import { deleteSession } from "@src/utils/session";
|
||||
import { deleteSession } from "@utils/session";
|
||||
import { ROUTES } from "@constants/routes";
|
||||
|
||||
export function ProtectedRoute({ children }: { children: ReactNode }) {
|
||||
@ -29,11 +29,11 @@ export function ProtectedRoute({ children }: { children: ReactNode }) {
|
||||
if (isLoading) {
|
||||
return;
|
||||
}
|
||||
if (!isAuthed) {
|
||||
if (!isAuthed || !isSuccess) {
|
||||
deleteSession();
|
||||
navigate(ROUTES.Login);
|
||||
}
|
||||
}, [isLoading, isAuthed]);
|
||||
}, [isLoading, isAuthed, isSuccess]);
|
||||
|
||||
return isSuccess ? <>{children}</> : null;
|
||||
return isSuccess && isAuthed ? <>{children}</> : null;
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
// Vitest Snapshot v1
|
||||
|
||||
exports[`atoms::ProtectedRoute > renders children if auth session exists 1`] = `
|
||||
<DocumentFragment>
|
||||
<div>
|
||||
Protected content
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`atoms::ProtectedRoute > skips rendering children and redirects to login if auth session can't be retrieved 1`] = `<DocumentFragment />`;
|
||||
|
||||
exports[`atoms::ProtectedRoute > skips rendering children and redirects to login if no auth session exists 1`] = `<DocumentFragment />`;
|
@ -21,8 +21,8 @@ exports[`atoms::Select > renders without exploding 1`] = `
|
||||
tabindex="-1"
|
||||
>
|
||||
<input
|
||||
defaultvalue=""
|
||||
type="hidden"
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
class="mantine-Select-wrapper mantine-12sbrde"
|
||||
@ -33,11 +33,11 @@ exports[`atoms::Select > renders without exploding 1`] = `
|
||||
autocomplete="nope"
|
||||
class="mantine-Select-defaultVariant mantine-Select-input mantine-93d3e4"
|
||||
data-mantine-stop-propagation="false"
|
||||
defaultvalue=""
|
||||
id="select"
|
||||
placeholder="Pick one"
|
||||
readonly=""
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
class="mantine-Select-rightSection mantine-14dm59e"
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
import { Stack } from "@mantine/core";
|
||||
import type { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||
import { ReactComponent as BtcIcon } from "@assets/btc.svg";
|
||||
import { TextInput } from ".";
|
||||
|
||||
export default {
|
||||
@ -23,17 +24,13 @@ export default {
|
||||
component: TextInput,
|
||||
} as ComponentMeta<typeof TextInput>;
|
||||
|
||||
const Template: ComponentStory<typeof TextInput> = (args) => {
|
||||
const Template: ComponentStory<typeof TextInput> = () => {
|
||||
return (
|
||||
<Stack>
|
||||
<TextInput {...args} />
|
||||
<TextInput id="name" placeholder="Your name" label="Full name" required />
|
||||
<TextInput id="btc" label="Bitcoin" icon={<BtcIcon />} />
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
id: "email",
|
||||
label: "Your email",
|
||||
placeholder: "johndoe@gmail.com",
|
||||
};
|
||||
|
@ -15,12 +15,13 @@
|
||||
// =============================================================================
|
||||
|
||||
import type { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||
import { NodeConnectSwitch } from ".";
|
||||
import { Text } from "@mantine/core";
|
||||
import { ReactComponent as CloudIcon } from "@assets/setting-cloud.svg";
|
||||
import { ReactComponent as ServerIcon } from "@assets/setting-server.svg";
|
||||
import { NodeConnectSwitch } from ".";
|
||||
|
||||
export default {
|
||||
title: "atoms/NodeConnectSwitch",
|
||||
title: "molecules/NodeConnectSwitch",
|
||||
component: NodeConnectSwitch,
|
||||
} as ComponentMeta<typeof NodeConnectSwitch>;
|
||||
|
||||
@ -28,13 +29,11 @@ const Template: ComponentStory<typeof NodeConnectSwitch> = () => {
|
||||
return (
|
||||
<NodeConnectSwitch>
|
||||
<NodeConnectSwitch.Method
|
||||
active={true}
|
||||
current={true}
|
||||
tabKey="local-node"
|
||||
label="Local Node"
|
||||
icon={<ServerIcon width="32px" height="62px" />}
|
||||
>
|
||||
Local Node
|
||||
<Text>Local Node</Text>
|
||||
</NodeConnectSwitch.Method>
|
||||
|
||||
<NodeConnectSwitch.Method
|
||||
@ -42,7 +41,7 @@ const Template: ComponentStory<typeof NodeConnectSwitch> = () => {
|
||||
label="Remote Node"
|
||||
icon={<CloudIcon width="58px" height="54px" />}
|
||||
>
|
||||
Remote Node
|
||||
<Text>Remote Node</Text>
|
||||
</NodeConnectSwitch.Method>
|
||||
</NodeConnectSwitch>
|
||||
);
|
@ -0,0 +1,112 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// 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 { describe, expect, it } from "vitest";
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { AppProviders } from "@atoms/AppProviders";
|
||||
import { ReactComponent as CloudIcon } from "@assets/setting-cloud.svg";
|
||||
import { ReactComponent as ServerIcon } from "@assets/setting-server.svg";
|
||||
import { NodeConnectSwitch } from ".";
|
||||
|
||||
describe("molecules::NodeConnectSwitch", () => {
|
||||
it("renders without exploding", () => {
|
||||
const { asFragment, unmount } = render(
|
||||
<AppProviders>
|
||||
<NodeConnectSwitch>
|
||||
<NodeConnectSwitch.Method
|
||||
active={true}
|
||||
current={true}
|
||||
tabKey="local-node"
|
||||
label="Local Node"
|
||||
icon={<ServerIcon width="32px" height="62px" />}
|
||||
>
|
||||
Local Node Content
|
||||
</NodeConnectSwitch.Method>
|
||||
|
||||
<NodeConnectSwitch.Method
|
||||
tabKey="remote-node"
|
||||
label="Remote Node"
|
||||
icon={<CloudIcon width="58px" height="54px" />}
|
||||
active={false}
|
||||
current={false}
|
||||
>
|
||||
Remote Node Content
|
||||
</NodeConnectSwitch.Method>
|
||||
</NodeConnectSwitch>
|
||||
</AppProviders>
|
||||
);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it("pre-renders active tab", () => {
|
||||
const { unmount } = render(
|
||||
<AppProviders>
|
||||
<NodeConnectSwitch active="local-node">
|
||||
<NodeConnectSwitch.Method
|
||||
current
|
||||
tabKey="local-node"
|
||||
label="Local Node"
|
||||
icon={<ServerIcon width="32px" height="62px" />}
|
||||
>
|
||||
Local Node Content
|
||||
</NodeConnectSwitch.Method>
|
||||
|
||||
<NodeConnectSwitch.Method
|
||||
tabKey="remote-node"
|
||||
label="Remote Node"
|
||||
icon={<CloudIcon width="58px" height="54px" />}
|
||||
>
|
||||
Remote Node Content
|
||||
</NodeConnectSwitch.Method>
|
||||
</NodeConnectSwitch>
|
||||
</AppProviders>
|
||||
);
|
||||
expect(screen.queryByText("Local Node Content")).toBeInTheDocument();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it("clicking a tab reveals its contents", () => {
|
||||
const { unmount } = render(
|
||||
<AppProviders>
|
||||
<NodeConnectSwitch>
|
||||
<NodeConnectSwitch.Method
|
||||
current
|
||||
tabKey="local-node"
|
||||
label="Local Node"
|
||||
icon={<ServerIcon width="32px" height="62px" />}
|
||||
>
|
||||
Local Node Content
|
||||
</NodeConnectSwitch.Method>
|
||||
|
||||
<NodeConnectSwitch.Method
|
||||
tabKey="remote-node"
|
||||
label="Remote Node"
|
||||
icon={<CloudIcon width="58px" height="54px" />}
|
||||
>
|
||||
Remote Node Content
|
||||
</NodeConnectSwitch.Method>
|
||||
</NodeConnectSwitch>
|
||||
</AppProviders>
|
||||
);
|
||||
const tabs = screen.queryAllByRole("tab");
|
||||
expect(tabs).toHaveLength(2);
|
||||
expect(screen.queryByText("Remote Node Content")).not.toBeInTheDocument();
|
||||
fireEvent.click(tabs[1]);
|
||||
expect(screen.queryByText("Remote Node Content")).toBeInTheDocument();
|
||||
unmount();
|
||||
});
|
||||
});
|
@ -73,12 +73,12 @@ export function NodeConnectSwitch({
|
||||
|
||||
return (
|
||||
<Box className={cx(classes.root, className)}>
|
||||
<div className={cx(classes.tabsListWrapper)}>{panes} </div>
|
||||
<Box className={cx(classes.tabsListWrapper)}>{panes}</Box>
|
||||
|
||||
{content && (
|
||||
<div role="tabpanel" className={classes.body} key={_activeTab}>
|
||||
<Box role="tabpanel" className={classes.body} key={_activeTab}>
|
||||
{content.props.children}
|
||||
</div>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
@ -1,12 +1,12 @@
|
||||
// Vitest Snapshot v1
|
||||
|
||||
exports[`atoms::NodeConnectSwitch > renders without exploding 1`] = `
|
||||
exports[`molecules::NodeConnectSwitch > renders without exploding 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mantine-15po0m8"
|
||||
>
|
||||
<div
|
||||
class="mantine-17do188"
|
||||
class="mantine-1d0mff5"
|
||||
>
|
||||
<button
|
||||
class="mantine-1lhe3fe"
|
||||
@ -114,7 +114,6 @@ exports[`atoms::NodeConnectSwitch > renders without exploding 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
@ -0,0 +1,39 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// 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 { describe, expect, it, vi } from "vitest";
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { AddPaymentMethodButton } from ".";
|
||||
|
||||
describe("molecules::AddPaymentMethodButton", () => {
|
||||
it("renders without exploding", () => {
|
||||
const spy = vi.fn();
|
||||
const { asFragment, unmount } = render(
|
||||
<AddPaymentMethodButton onClick={spy} />
|
||||
);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it("calls onClick", () => {
|
||||
const spy = vi.fn();
|
||||
const { unmount } = render(<AddPaymentMethodButton onClick={spy} />);
|
||||
expect(spy).to.not.toHaveBeenCalled();
|
||||
fireEvent.click(screen.getByRole("button"));
|
||||
expect(spy).to.toHaveBeenCalledTimes(1);
|
||||
unmount();
|
||||
});
|
||||
});
|
@ -0,0 +1,56 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// 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 { Stack } from "@mantine/core";
|
||||
import type { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||
import {
|
||||
CryptoCurrencyAccountPayload,
|
||||
PaymentAccount,
|
||||
PaymentAccountPayload,
|
||||
} from "haveno-ts";
|
||||
import { AddPaymentMethodButton, PaymentMethodCard } from ".";
|
||||
|
||||
export default {
|
||||
title: "molecules/PaymentMethodCard",
|
||||
component: PaymentMethodCard,
|
||||
} as ComponentMeta<typeof PaymentMethodCard>;
|
||||
|
||||
const Template: ComponentStory<typeof PaymentMethodCard> = () => {
|
||||
return (
|
||||
<Stack>
|
||||
<PaymentMethodCard data={paymentAccount1} />
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
const Template2: ComponentStory<typeof PaymentMethodCard> = () => {
|
||||
return (
|
||||
<Stack>
|
||||
<AddPaymentMethodButton onClick={() => console.log("onClick called")} />
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
export const AddButton = Template2.bind({});
|
||||
|
||||
const paymentAccount1 = new PaymentAccount();
|
||||
paymentAccount1.setAccountName("BTC Account 1");
|
||||
const cryptoAccPayload1 = new CryptoCurrencyAccountPayload();
|
||||
cryptoAccPayload1.setAddress("01234567abcdef");
|
||||
const paymentAccPayload1 = new PaymentAccountPayload();
|
||||
paymentAccPayload1.setCryptoCurrencyAccountPayload(cryptoAccPayload1);
|
||||
paymentAccount1.setPaymentAccountPayload(paymentAccPayload1);
|
@ -16,38 +16,30 @@
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { render } from "@testing-library/react";
|
||||
import {
|
||||
CryptoCurrencyAccountPayload,
|
||||
PaymentAccount,
|
||||
PaymentAccountPayload,
|
||||
} from "haveno-ts";
|
||||
import { AppProviders } from "@atoms/AppProviders";
|
||||
import { ReactComponent as CloudIcon } from "@assets/setting-cloud.svg";
|
||||
import { ReactComponent as ServerIcon } from "@assets/setting-server.svg";
|
||||
import { NodeConnectSwitch } from ".";
|
||||
import { PaymentMethodCard } from ".";
|
||||
|
||||
describe("atoms::NodeConnectSwitch", () => {
|
||||
describe("molecules::PaymentMethodCard", () => {
|
||||
it("renders without exploding", () => {
|
||||
const { asFragment } = render(
|
||||
const { asFragment, unmount } = render(
|
||||
<AppProviders>
|
||||
<NodeConnectSwitch>
|
||||
<NodeConnectSwitch.Method
|
||||
active={true}
|
||||
current={true}
|
||||
tabKey={"local-node"}
|
||||
label="Local Node"
|
||||
icon={<ServerIcon width="32px" height="62px" />}
|
||||
>
|
||||
Local Node
|
||||
</NodeConnectSwitch.Method>
|
||||
|
||||
<NodeConnectSwitch.Method
|
||||
tabKey={"remote-node"}
|
||||
label="Remote Node"
|
||||
icon={<CloudIcon width="58px" height="54px" />}
|
||||
active={false}
|
||||
current={false}
|
||||
>
|
||||
Remote Node
|
||||
</NodeConnectSwitch.Method>
|
||||
</NodeConnectSwitch>
|
||||
<PaymentMethodCard data={paymentAccount1} />
|
||||
</AppProviders>
|
||||
);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
unmount();
|
||||
});
|
||||
});
|
||||
|
||||
const paymentAccount1 = new PaymentAccount();
|
||||
paymentAccount1.setAccountName("BTC Account 1");
|
||||
const cryptoAccPayload1 = new CryptoCurrencyAccountPayload();
|
||||
cryptoAccPayload1.setAddress("01234567abcdef");
|
||||
const paymentAccPayload1 = new PaymentAccountPayload();
|
||||
paymentAccPayload1.setCryptoCurrencyAccountPayload(cryptoAccPayload1);
|
||||
paymentAccount1.setPaymentAccountPayload(paymentAccPayload1);
|
@ -31,7 +31,7 @@ import {
|
||||
getPaymentAccountLogo,
|
||||
getPaymentAccountName,
|
||||
getPaymentAccountNumber,
|
||||
} from "@src/utils/payment-account";
|
||||
} from "@utils/payment-account";
|
||||
|
||||
interface PaymentMethodCardProps {
|
||||
data: PaymentAccount;
|
||||
|
@ -0,0 +1,24 @@
|
||||
// Vitest Snapshot v1
|
||||
|
||||
exports[`molecules::AddPaymentMethodButton > renders without exploding 1`] = `
|
||||
<DocumentFragment>
|
||||
<button
|
||||
class="mantine-UnstyledButton-root mantine-17b3lsu"
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
fill="none"
|
||||
height="1em"
|
||||
viewBox="0 0 28 28"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M14 0C6.3 0 0 6.3 0 14C0 21.7 6.3 28 14 28C21.7 28 28 21.7 28 14C28 6.3 21.7 0 14 0ZM21 15H15V21C15 21.55 14.55 22 14 22C13.45 22 13 21.55 13 21V15H7C6.45 15 6 14.55 6 14C6 13.45 6.45 13 7 13C7 13 10.05 13 13 13V7C13 6.45 13.45 6 14 6C14.55 6 15 6.45 15 7V13C17.95 13 21 13 21 13C21.55 13 22 13.45 22 14C22 14.55 21.55 15 21 15Z"
|
||||
fill="#0B65DA"
|
||||
fill-opacity="0.25"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</DocumentFragment>
|
||||
`;
|
@ -0,0 +1,67 @@
|
||||
// Vitest Snapshot v1
|
||||
|
||||
exports[`molecules::PaymentMethodCard > renders without exploding 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mantine-1lb8n9h"
|
||||
>
|
||||
<div
|
||||
class="mantine-Stack-root mantine-lfk3cq"
|
||||
>
|
||||
<div
|
||||
class="mantine-Group-root mantine-19jxmdp"
|
||||
>
|
||||
<div
|
||||
class="mantine-Group-root mantine-Group-child mantine-zah578"
|
||||
>
|
||||
<svg
|
||||
class="mantine-gnzaph mantine-Group-child"
|
||||
fill="none"
|
||||
height="1em"
|
||||
viewBox="0 0 28 28"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M27.578 17.3866C25.7081 24.8866 18.1118 29.4511 10.6109 27.5808C3.11301 25.7109 -1.45142 18.1141 0.419327 10.6145C2.28833 3.11358 9.88464 -1.45129 17.3834 0.418582C24.8839 2.28846 29.4479 9.88608 27.578 17.3866Z"
|
||||
fill="#F7931A"
|
||||
/>
|
||||
<path
|
||||
d="M20.173 12.0055C20.4516 10.1426 19.0333 9.14117 17.0938 8.47311L17.723 5.94961L16.1869 5.5668L15.5744 8.0238C15.1706 7.92317 14.7558 7.82824 14.3437 7.73417L14.9606 5.26099L13.4254 4.87817L12.7958 7.4008C12.4616 7.32467 12.1335 7.24942 11.815 7.17024L11.8167 7.16236L9.69833 6.63342L9.28971 8.27405C9.28971 8.27405 10.4294 8.53524 10.4053 8.55142C11.0275 8.70674 11.1399 9.11842 11.1211 9.4448L10.4045 12.3196C10.4473 12.3305 10.5029 12.3463 10.5641 12.3708C10.513 12.3581 10.4583 12.3441 10.4018 12.3305L9.39733 16.3577C9.32121 16.5467 9.12827 16.8302 8.69339 16.7226C8.70871 16.7449 7.57689 16.4439 7.57689 16.4439L6.81433 18.2022L8.81327 18.7005C9.18514 18.7937 9.54958 18.8913 9.90833 18.9832L9.27264 21.5355L10.807 21.9184L11.4365 19.3931C11.8556 19.5069 12.2625 19.6119 12.6606 19.7107L12.0333 22.2242L13.5693 22.607L14.205 20.0594C16.8243 20.5551 18.794 20.3552 19.623 17.9861C20.2911 16.0786 19.5898 14.9783 18.2116 14.2608C19.2153 14.0294 19.9713 13.3692 20.173 12.0055ZM16.6633 16.9269C16.1886 18.8344 12.977 17.8032 11.9357 17.5447L12.7792 14.1632C13.8205 14.4231 17.1595 14.9376 16.6633 16.9269ZM17.1385 11.9779C16.7053 13.713 14.0322 12.8315 13.1651 12.6154L13.9298 9.54849C14.797 9.76461 17.5895 10.168 17.1385 11.9779Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
<div
|
||||
class="mantine-Text-root mantine-Group-child mantine-xrxbf2"
|
||||
>
|
||||
BTC
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="mantine-UnstyledButton-root mantine-Group-child mantine-1e6tu8r"
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
class="mantine-ezordu"
|
||||
fill="none"
|
||||
height="1em"
|
||||
viewBox="0 0 15 4"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M9.375 1.875C9.375 2.925 8.5125 3.75 7.5 3.75C6.4875 3.75 5.625 2.8875 5.625 1.875C5.625 0.8625 6.4875 0 7.5 0C8.5125 0 9.375 0.8625 9.375 1.875ZM13.125 0C12.075 0 11.25 0.8625 11.25 1.875C11.25 2.8875 12.075 3.75 13.125 3.75C14.175 3.75 15 2.8875 15 1.875C15 0.8625 14.175 0 13.125 0ZM1.875 0C0.825 0 0 0.8625 0 1.875C0 2.8875 0.8625 3.75 1.875 3.75C2.8875 3.75 3.75 2.8875 3.75 1.875C3.75 0.8625 2.925 0 1.875 0Z"
|
||||
fill="#111111"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="mantine-Text-root mantine-17kke86"
|
||||
>
|
||||
01234567abcdef
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
@ -14,4 +14,5 @@
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
// TODO @subir: move this to @constants/currencies
|
||||
export type SupportedCurrencies = "BTC" | "ETH" | "EUR";
|
||||
|
@ -0,0 +1,34 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// 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 { Container } from "@mantine/core";
|
||||
import type { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||
import { ReadyToUse } from ".";
|
||||
|
||||
export default {
|
||||
title: "molecules/ReadyToUse",
|
||||
component: ReadyToUse,
|
||||
} as ComponentMeta<typeof ReadyToUse>;
|
||||
|
||||
const Template: ComponentStory<typeof ReadyToUse> = () => {
|
||||
return (
|
||||
<Container>
|
||||
<ReadyToUse onSubmit={() => console.log("onSubmit called")} />
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
@ -0,0 +1,37 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// 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 { describe, expect, it, vi } from "vitest";
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { ReadyToUse } from ".";
|
||||
|
||||
describe("molecules::ReadyToUse", () => {
|
||||
it("renders without exploding", () => {
|
||||
const spy = vi.fn();
|
||||
const { asFragment, unmount } = render(<ReadyToUse onSubmit={spy} />);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it("calls onSubmit", () => {
|
||||
const spy = vi.fn();
|
||||
const { unmount } = render(<ReadyToUse onSubmit={spy} />);
|
||||
expect(spy).to.not.toHaveBeenCalled();
|
||||
fireEvent.click(screen.getByRole("button"));
|
||||
expect(spy).to.toHaveBeenCalledTimes(1);
|
||||
unmount();
|
||||
});
|
||||
});
|
@ -0,0 +1,41 @@
|
||||
// Vitest Snapshot v1
|
||||
|
||||
exports[`molecules::ReadyToUse > renders without exploding 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mantine-Stack-root mantine-lfk3cq"
|
||||
>
|
||||
<h1
|
||||
class="mantine-Title-root mantine-xz2ygd"
|
||||
>
|
||||
Haveno is ready for use.
|
||||
</h1>
|
||||
<div
|
||||
class="mantine-Text-root mantine-1w4ewzw"
|
||||
>
|
||||
You’ve succesfully set up Haveno. Please note that to be able to trade, you need to deposit Monero in your Haveno wallet and set up a payment account.
|
||||
</div>
|
||||
<div
|
||||
class="mantine-63n06h"
|
||||
/>
|
||||
<div
|
||||
class="mantine-Group-root mantine-19jxmdp"
|
||||
>
|
||||
<button
|
||||
class="mantine-Button-filled mantine-Button-root mantine-Group-child mantine-1bqp2m7"
|
||||
type="submit"
|
||||
>
|
||||
<div
|
||||
class="mantine-3xbgk5 mantine-Button-inner"
|
||||
>
|
||||
<span
|
||||
class="mantine-qo1k2 mantine-Button-label"
|
||||
>
|
||||
Start using Haveno
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
@ -16,19 +16,19 @@
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { render } from "@testing-library/react";
|
||||
import { AppProviders } from "@atoms/AppProviders";
|
||||
import { SecondarySidebar, SecondarySidebarItem } from "./";
|
||||
import { ThemeProvider } from "@atoms/AppProviders/ThemeProvider";
|
||||
|
||||
describe("molecules::SecondarySidebar", () => {
|
||||
it("renders without exploding", () => {
|
||||
const { asFragment } = render(
|
||||
<ThemeProvider>
|
||||
<AppProviders>
|
||||
<SecondarySidebar>
|
||||
<SecondarySidebarItem label="Active item" isActive={true} />
|
||||
<SecondarySidebarItem label="Inactive item" isActive={false} />
|
||||
<SecondarySidebarItem label="Active item" isActive={true} />
|
||||
</SecondarySidebar>
|
||||
</ThemeProvider>
|
||||
</AppProviders>
|
||||
);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
@ -25,7 +25,9 @@ interface SecondarySidebarProps {
|
||||
* @param {SecondarySidebarProps}
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export function SecondarySidebar({ children }: SecondarySidebarProps) {
|
||||
export function SecondarySidebar({
|
||||
children,
|
||||
}: SecondarySidebarProps): JSX.Element {
|
||||
const { classes } = useStyles();
|
||||
|
||||
return (
|
||||
|
@ -14,12 +14,14 @@
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import { useNavLinkActive } from "@hooks/misc/useNavLinkActive";
|
||||
import { UnstyledButton, Group, Text, createStyles } from "@mantine/core";
|
||||
|
||||
interface SecondarySidebarItemProps {
|
||||
isActive?: boolean;
|
||||
label: string;
|
||||
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
route?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -31,8 +33,10 @@ export function SecondarySidebarItem({
|
||||
isActive = false,
|
||||
label,
|
||||
onClick,
|
||||
}: SecondarySidebarItemProps) {
|
||||
const { classes } = useStyles({ isActive });
|
||||
route,
|
||||
}: SecondarySidebarItemProps): JSX.Element {
|
||||
const isRouteActive = useNavLinkActive({ to: route });
|
||||
const { classes } = useStyles({ isActive: isRouteActive || isActive });
|
||||
|
||||
return (
|
||||
<UnstyledButton className={classes.button} onClick={onClick}>
|
||||
|
@ -18,7 +18,7 @@ import type { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||
import { AccountSidebar } from "./AccountSidebar";
|
||||
|
||||
export default {
|
||||
title: "molecules/AccountSidebar",
|
||||
title: "organisms/AccountSidebar",
|
||||
component: AccountSidebar,
|
||||
} as ComponentMeta<typeof AccountSidebar>;
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { render } from "@testing-library/react";
|
||||
import { Routes, Route } from "react-router-dom";
|
||||
import { AppProviders } from "@atoms/AppProviders";
|
||||
import { AccountSidebar } from "./AccountSidebar";
|
||||
|
||||
@ -24,9 +23,7 @@ describe("molecules::AccountSidebar", () => {
|
||||
it("renders without exploding", () => {
|
||||
const { asFragment } = render(
|
||||
<AppProviders>
|
||||
<Routes>
|
||||
<Route path="/" element={<AccountSidebar />} />
|
||||
</Routes>
|
||||
<AccountSidebar />
|
||||
</AppProviders>
|
||||
);
|
||||
expect(asFragment()).toMatchSnapshot();
|
@ -16,15 +16,18 @@
|
||||
|
||||
import { createStyles, Box, Title } from "@mantine/core";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { SecondarySidebar } from "@molecules/SecondarySidebar";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import {
|
||||
SecondarySidebar,
|
||||
SecondarySidebarItem,
|
||||
} from "@molecules/SecondarySidebar";
|
||||
import { LangKeys } from "@constants/lang";
|
||||
import { WIDTH } from "./_constants";
|
||||
import { useGetAccountSidebarMenu } from "./_hooks";
|
||||
import { AccountSidebarItem } from "./AccountSidebarItem";
|
||||
|
||||
export function AccountSidebar() {
|
||||
const { classes } = useStyles();
|
||||
|
||||
const navigate = useNavigate();
|
||||
const menu = useGetAccountSidebarMenu();
|
||||
|
||||
return (
|
||||
@ -35,10 +38,13 @@ export function AccountSidebar() {
|
||||
|
||||
<SecondarySidebar>
|
||||
{menu.map((item) => (
|
||||
<AccountSidebarItem
|
||||
<SecondarySidebarItem
|
||||
key={item.label}
|
||||
label={item.label}
|
||||
route={item.route}
|
||||
onClick={() => {
|
||||
navigate(item.route);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</SecondarySidebar>
|
@ -20,15 +20,9 @@ import { useForm, joiResolver } from "@mantine/form";
|
||||
import Joi from "joi";
|
||||
import { Button } from "@atoms/Buttons";
|
||||
import { Select } from "@atoms/Select";
|
||||
import { TextInput } from "@atoms/TextInput";
|
||||
import { SupportedCurrencies } from "@constants/currencies";
|
||||
import { PaymentMethods as _PaymentMethods } from "@constants/payment-methods";
|
||||
import { TextInput } from "@atoms/TextInput";
|
||||
|
||||
interface FormValues {
|
||||
currency: string;
|
||||
paymentMethod: string;
|
||||
accountNumber: string;
|
||||
}
|
||||
|
||||
export function AddPaymentMethod() {
|
||||
const { getInputProps, onSubmit, setFieldValue, values } =
|
||||
@ -41,7 +35,7 @@ export function AddPaymentMethod() {
|
||||
},
|
||||
});
|
||||
|
||||
const PaymentMethods = useMemo(() => {
|
||||
const paymentMethods = useMemo(() => {
|
||||
if (!values.currency) {
|
||||
return [];
|
||||
}
|
||||
@ -61,6 +55,7 @@ export function AddPaymentMethod() {
|
||||
.sort((a, b) => (a.label > b.label ? 1 : -1));
|
||||
}, [values?.currency]);
|
||||
|
||||
// TODO @subir
|
||||
const handleSubmit = (values: FormValues) => console.log(values);
|
||||
|
||||
useEffect(() => {
|
||||
@ -86,7 +81,7 @@ export function AddPaymentMethod() {
|
||||
<Collapse in={Boolean(values.currency)}>
|
||||
<Select
|
||||
creatable
|
||||
data={PaymentMethods}
|
||||
data={paymentMethods}
|
||||
id="paymentMethod"
|
||||
label="Select your preferred payment method"
|
||||
placeholder="Pick one"
|
||||
@ -114,6 +109,12 @@ export function AddPaymentMethod() {
|
||||
);
|
||||
}
|
||||
|
||||
interface FormValues {
|
||||
currency: string;
|
||||
paymentMethod: string;
|
||||
accountNumber: string;
|
||||
}
|
||||
|
||||
const schema = Joi.object<FormValues>({
|
||||
currency: Joi.string().required(),
|
||||
paymentMethod: Joi.string().required(),
|
||||
|
@ -28,8 +28,8 @@ exports[`organisms::AddPaymentMethod > renders without exploding 1`] = `
|
||||
tabindex="-1"
|
||||
>
|
||||
<input
|
||||
defaultvalue=""
|
||||
type="hidden"
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
class="mantine-Select-wrapper mantine-12sbrde"
|
||||
@ -40,11 +40,11 @@ exports[`organisms::AddPaymentMethod > renders without exploding 1`] = `
|
||||
autocomplete="nope"
|
||||
class="mantine-Select-defaultVariant mantine-Select-input mantine-93d3e4"
|
||||
data-mantine-stop-propagation="false"
|
||||
defaultvalue=""
|
||||
id="currency"
|
||||
placeholder="Pick one"
|
||||
readonly=""
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
class="mantine-Select-rightSection mantine-14dm59e"
|
||||
@ -95,8 +95,8 @@ exports[`organisms::AddPaymentMethod > renders without exploding 1`] = `
|
||||
tabindex="-1"
|
||||
>
|
||||
<input
|
||||
defaultvalue=""
|
||||
type="hidden"
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
class="mantine-Select-wrapper mantine-12sbrde"
|
||||
@ -107,10 +107,10 @@ exports[`organisms::AddPaymentMethod > renders without exploding 1`] = `
|
||||
autocomplete="nope"
|
||||
class="mantine-Select-defaultVariant mantine-Select-input mantine-16ko5f2"
|
||||
data-mantine-stop-propagation="false"
|
||||
defaultvalue=""
|
||||
id="paymentMethod"
|
||||
placeholder="Pick one"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
class="mantine-Select-rightSection mantine-14dm59e"
|
||||
@ -160,9 +160,9 @@ exports[`organisms::AddPaymentMethod > renders without exploding 1`] = `
|
||||
<input
|
||||
aria-invalid="false"
|
||||
class="mantine-TextInput-defaultVariant mantine-TextInput-input mantine-dagq8e"
|
||||
defaultvalue=""
|
||||
id="accountNumber"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -14,21 +14,16 @@
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import { Stack } from "@mantine/core";
|
||||
import type { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||
import { Wallet } from ".";
|
||||
import { ChangePassword } from ".";
|
||||
|
||||
export default {
|
||||
title: "pages/Wallet",
|
||||
component: Wallet,
|
||||
} as ComponentMeta<typeof Wallet>;
|
||||
title: "organisms/Change Password",
|
||||
component: ChangePassword,
|
||||
} as ComponentMeta<typeof ChangePassword>;
|
||||
|
||||
const Template: ComponentStory<typeof Wallet> = () => {
|
||||
return (
|
||||
<Stack>
|
||||
<Wallet />
|
||||
</Stack>
|
||||
);
|
||||
const Template: ComponentStory<typeof ChangePassword> = () => {
|
||||
return <ChangePassword />;
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
@ -18,7 +18,7 @@ import { FormattedMessage } from "react-intl";
|
||||
import { Stack, Box, Group } from "@mantine/core";
|
||||
import { useForm, joiResolver } from "@mantine/form";
|
||||
import { showNotification } from "@mantine/notifications";
|
||||
import { TextInput } from "@atoms/TextInput";
|
||||
import { PasswordInput } from "@atoms/PasswordInput";
|
||||
import { LangKeys } from "@constants/lang";
|
||||
import { Button } from "@atoms/Buttons";
|
||||
import { useChangePassword } from "@hooks/storage/useChangePassword";
|
||||
@ -68,9 +68,8 @@ export function ChangePassword() {
|
||||
<Box>
|
||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||
<Stack spacing="lg">
|
||||
<TextInput
|
||||
<PasswordInput
|
||||
id="password"
|
||||
type="password"
|
||||
required
|
||||
label={
|
||||
<FormattedMessage
|
||||
@ -80,10 +79,9 @@ export function ChangePassword() {
|
||||
}
|
||||
{...form.getInputProps("newPassword")}
|
||||
/>
|
||||
<TextInput
|
||||
<PasswordInput
|
||||
id="confirmPassword"
|
||||
required
|
||||
type="password"
|
||||
label={
|
||||
<FormattedMessage
|
||||
id={LangKeys.AccountSecurityFieldRepeatPassword}
|
||||
@ -92,9 +90,8 @@ export function ChangePassword() {
|
||||
}
|
||||
{...form.getInputProps("confirmPassword")}
|
||||
/>
|
||||
<TextInput
|
||||
<PasswordInput
|
||||
id="currentPassword"
|
||||
type="password"
|
||||
required
|
||||
label={
|
||||
<FormattedMessage
|
||||
|
@ -0,0 +1,33 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// 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 type { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||
import { SelectMoneroNode } from ".";
|
||||
|
||||
export default {
|
||||
title: "organisms/Select Monero Node",
|
||||
component: SelectMoneroNode,
|
||||
} as ComponentMeta<typeof SelectMoneroNode>;
|
||||
|
||||
const Template: ComponentStory<typeof SelectMoneroNode> = (args) => {
|
||||
return <SelectMoneroNode {...args} />;
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
onGoBack: () => console.log("go back"),
|
||||
onNext: () => console.log("next"),
|
||||
};
|
@ -0,0 +1,33 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// 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 type { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||
import { SetPassword } from ".";
|
||||
|
||||
export default {
|
||||
title: "organisms/Set Password",
|
||||
component: SetPassword,
|
||||
} as ComponentMeta<typeof SetPassword>;
|
||||
|
||||
const Template: ComponentStory<typeof SetPassword> = (args) => {
|
||||
return <SetPassword {...args} />;
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
onGoBack: () => console.log("go back"),
|
||||
onNext: (value: string) => console.log(value),
|
||||
};
|
@ -0,0 +1,86 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// 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 { describe, expect, it, vi } from "vitest";
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { AppProviders } from "@atoms/AppProviders";
|
||||
import { SetPassword } from ".";
|
||||
|
||||
describe("organisms::SetPassword", () => {
|
||||
it("renders without exploding", () => {
|
||||
const onBackSpy = vi.fn();
|
||||
const onNextSpy = vi.fn();
|
||||
const { asFragment, unmount } = render(
|
||||
<AppProviders>
|
||||
<SetPassword onGoBack={onBackSpy} onNext={onNextSpy} />
|
||||
</AppProviders>
|
||||
);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it("calls onGoBack", async () => {
|
||||
const onBackSpy = vi.fn();
|
||||
const onNextSpy = vi.fn();
|
||||
const { unmount } = render(
|
||||
<AppProviders>
|
||||
<SetPassword onGoBack={onBackSpy} onNext={onNextSpy} />
|
||||
</AppProviders>
|
||||
);
|
||||
expect(onBackSpy).to.not.toHaveBeenCalled();
|
||||
fireEvent.click(await screen.findByLabelText("Click to go back"));
|
||||
expect(onBackSpy).to.toHaveBeenCalledTimes(1);
|
||||
expect(onNextSpy).to.not.toHaveBeenCalled();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it("blocks submit if validation fails", async () => {
|
||||
const onBackSpy = vi.fn();
|
||||
const onNextSpy = vi.fn();
|
||||
|
||||
const { unmount } = render(
|
||||
<AppProviders>
|
||||
<SetPassword onGoBack={onBackSpy} onNext={onNextSpy} />
|
||||
</AppProviders>
|
||||
);
|
||||
expect(onNextSpy).to.not.toHaveBeenCalled();
|
||||
fireEvent.click(await screen.findByLabelText("Click to submit"));
|
||||
expect(onNextSpy).to.not.toHaveBeenCalled();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it("calls onSubmit if validation succeeds", async () => {
|
||||
const onBackSpy = vi.fn();
|
||||
const onNextSpy = vi.fn();
|
||||
const user = userEvent.setup();
|
||||
const { unmount } = render(
|
||||
<AppProviders>
|
||||
<SetPassword onGoBack={onBackSpy} onNext={onNextSpy} />
|
||||
</AppProviders>
|
||||
);
|
||||
await user.type(screen.getByLabelText("Enter password"), "Qwe$9999", {
|
||||
skipAutoClose: true,
|
||||
});
|
||||
await user.type(screen.getByLabelText("Repeat password"), "Qwe$9999", {
|
||||
skipAutoClose: true,
|
||||
});
|
||||
expect(onNextSpy).to.not.toHaveBeenCalled();
|
||||
fireEvent.submit(screen.getByLabelText("Click to submit"));
|
||||
expect(onNextSpy).to.toHaveBeenCalledTimes(1);
|
||||
unmount();
|
||||
});
|
||||
});
|
@ -23,9 +23,9 @@ import { Button, TextButton } from "@atoms/Buttons";
|
||||
import { LangKeys } from "@constants/lang";
|
||||
|
||||
interface SetPasswordProps {
|
||||
value: string;
|
||||
onGoBack: () => void;
|
||||
onNext: (password: string) => void;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
export function SetPassword(props: SetPasswordProps) {
|
||||
@ -43,7 +43,7 @@ export function SetPassword(props: SetPasswordProps) {
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={onSubmit(handleSubmit)}>
|
||||
<form data-testid="change-password-form" onSubmit={onSubmit(handleSubmit)}>
|
||||
<Stack>
|
||||
<Container>
|
||||
<Heading order={1} stringId={LangKeys.CreatePassword}>
|
||||
@ -55,21 +55,31 @@ export function SetPassword(props: SetPasswordProps) {
|
||||
password.
|
||||
</BodyText>
|
||||
<TextInput
|
||||
aria-label="Enter password"
|
||||
autoFocus={false}
|
||||
id="password"
|
||||
label="Password"
|
||||
tabIndex={1}
|
||||
type="password"
|
||||
{...getInputProps("password")}
|
||||
/>
|
||||
<TextInput
|
||||
aria-label="Repeat password"
|
||||
autoFocus={false}
|
||||
id="repeatPassword"
|
||||
label="Repeat password"
|
||||
tabIndex={2}
|
||||
type="password"
|
||||
{...getInputProps("repeatPassword")}
|
||||
/>
|
||||
<Space h="lg" />
|
||||
<Group position="apart">
|
||||
<TextButton onClick={onGoBack}>Go Back</TextButton>
|
||||
<Button type="submit">Next</Button>
|
||||
<TextButton aria-label="Click to go back" onClick={onGoBack}>
|
||||
Go Back
|
||||
</TextButton>
|
||||
<Button aria-label="Click to submit" type="submit">
|
||||
Next
|
||||
</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</form>
|
||||
|
@ -0,0 +1,109 @@
|
||||
// Vitest Snapshot v1
|
||||
|
||||
exports[`organisms::SetPassword > renders without exploding 1`] = `
|
||||
<DocumentFragment>
|
||||
<form
|
||||
data-testid="change-password-form"
|
||||
>
|
||||
<div
|
||||
class="mantine-Stack-root mantine-lfk3cq"
|
||||
>
|
||||
<div
|
||||
class="mantine-Container-root mantine-me1cdz"
|
||||
>
|
||||
<h1
|
||||
class="mantine-Title-root mantine-1rawl7h"
|
||||
>
|
||||
Create password
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mantine-Text-root mantine-1ourfup"
|
||||
>
|
||||
All your data is stored locally on your machine. Haveno uses solely a password.
|
||||
</div>
|
||||
<div
|
||||
class="mantine-TextInput-root mantine-14qek68"
|
||||
>
|
||||
<label
|
||||
class="mantine-TextInput-label mantine-1bjo575"
|
||||
for="password"
|
||||
id="password-label"
|
||||
>
|
||||
Password
|
||||
</label>
|
||||
<div
|
||||
class="mantine-TextInput-wrapper mantine-12sbrde"
|
||||
>
|
||||
<input
|
||||
aria-invalid="false"
|
||||
aria-label="Enter password"
|
||||
class="mantine-TextInput-defaultVariant mantine-TextInput-input mantine-nk8491"
|
||||
id="password"
|
||||
tabindex="1"
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mantine-TextInput-root mantine-14qek68"
|
||||
>
|
||||
<label
|
||||
class="mantine-TextInput-label mantine-1bjo575"
|
||||
for="repeatPassword"
|
||||
id="repeatPassword-label"
|
||||
>
|
||||
Repeat password
|
||||
</label>
|
||||
<div
|
||||
class="mantine-TextInput-wrapper mantine-12sbrde"
|
||||
>
|
||||
<input
|
||||
aria-invalid="false"
|
||||
aria-label="Repeat password"
|
||||
class="mantine-TextInput-defaultVariant mantine-TextInput-input mantine-nk8491"
|
||||
id="repeatPassword"
|
||||
tabindex="2"
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mantine-63n06h"
|
||||
/>
|
||||
<div
|
||||
class="mantine-Group-root mantine-19jxmdp"
|
||||
>
|
||||
<button
|
||||
aria-label="Click to go back"
|
||||
class="mantine-UnstyledButton-root mantine-Group-child mantine-1smyfmn"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="mantine-Text-root mantine-1mwiqnz"
|
||||
>
|
||||
Go Back
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
aria-label="Click to submit"
|
||||
class="mantine-Button-filled mantine-Button-root mantine-Group-child mantine-s2bdxi"
|
||||
type="submit"
|
||||
>
|
||||
<div
|
||||
class="mantine-3xbgk5 mantine-Button-inner"
|
||||
>
|
||||
<span
|
||||
class="mantine-qo1k2 mantine-Button-label"
|
||||
>
|
||||
Next
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</DocumentFragment>
|
||||
`;
|
@ -0,0 +1,33 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// 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 type { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||
import { SetPrimaryFiat } from ".";
|
||||
|
||||
export default {
|
||||
title: "organisms/Set Primary Fiat",
|
||||
component: SetPrimaryFiat,
|
||||
} as ComponentMeta<typeof SetPrimaryFiat>;
|
||||
|
||||
const Template: ComponentStory<typeof SetPrimaryFiat> = (args) => {
|
||||
return <SetPrimaryFiat {...args} />;
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
onGoBack: () => console.log("go back"),
|
||||
onNext: (value: string) => console.log(value),
|
||||
};
|
@ -25,7 +25,7 @@ import { SupportedCurrencies } from "@constants/currencies";
|
||||
interface SetSetPrimaryFiatProps {
|
||||
onGoBack: () => void;
|
||||
onNext: (fiat: string) => void;
|
||||
value: string;
|
||||
value?: string;
|
||||
}
|
||||
|
||||
export function SetPrimaryFiat(props: SetSetPrimaryFiatProps) {
|
||||
|
@ -14,12 +14,23 @@
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import { render } from "@testing-library/react";
|
||||
import { AppProviders } from "@atoms/AppProviders";
|
||||
import { SyncStatus } from "@constants/sync-status";
|
||||
import { Sidebar } from ".";
|
||||
|
||||
describe("molecules::Sidebar", () => {
|
||||
beforeAll(() => {
|
||||
vi.mock("@hooks/haveno/useSyncStatus", () => ({
|
||||
useSyncStatus: () => ({
|
||||
isLoading: false,
|
||||
isSuccess: true,
|
||||
data: SyncStatus.NotSynced,
|
||||
}),
|
||||
}));
|
||||
});
|
||||
|
||||
it("renders without exploding", () => {
|
||||
const { asFragment } = render(
|
||||
<AppProviders>
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
import { Group, createStyles, Box } from "@mantine/core";
|
||||
import { NavbarLayout } from "@templates/NavbarLayout";
|
||||
import { AccountSidebar } from "@molecules/AccountSidebar";
|
||||
import { AccountSidebar } from "@organisms/AccountSidebar";
|
||||
|
||||
interface AccountContentProps {
|
||||
children: JSX.Element | JSX.Element[];
|
||||
|
@ -19,7 +19,9 @@ import { ReactComponent as EthLogo } from "@assets/eth.svg";
|
||||
import { ReactComponent as EurLogo } from "@assets/eur.svg";
|
||||
import { PaymentMethodIds } from "./payment-methods";
|
||||
|
||||
export type SupportedFiat = "USD" | "ETH" | "GBP";
|
||||
export type SupportedFiat = "USD" | "EUR" | "GBP";
|
||||
|
||||
export type SupportedCrypto = "XMR" | "BTC" | "ETH";
|
||||
|
||||
export const SupportedCurrencies = [
|
||||
{
|
||||
|
@ -21,7 +21,12 @@ import { useHavenoClient } from "./useHavenoClient";
|
||||
export function useIsMoneroNodeRunning() {
|
||||
const client = useHavenoClient();
|
||||
|
||||
return useQuery<boolean, Error>(QueryKeys.MoneroNodeIsRunning, () =>
|
||||
client.isMoneroNodeRunning()
|
||||
);
|
||||
return useQuery<boolean, Error>(QueryKeys.MoneroNodeIsRunning, async () => {
|
||||
try {
|
||||
const value = await client.isMoneroNodeRunning();
|
||||
return value;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -14,12 +14,13 @@
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import { useMemo } from "react";
|
||||
import { useResolvedPath, useLocation } from "react-router-dom";
|
||||
|
||||
interface LinkItemActiveProps {
|
||||
to: string;
|
||||
caseSensitive?: boolean;
|
||||
end?: boolean;
|
||||
to?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -31,22 +32,29 @@ export const useNavLinkActive = ({
|
||||
caseSensitive = false,
|
||||
end = false,
|
||||
to,
|
||||
}: LinkItemActiveProps) => {
|
||||
}: LinkItemActiveProps): boolean => {
|
||||
const location = useLocation();
|
||||
const path = useResolvedPath(to);
|
||||
const path = useResolvedPath(to ?? "");
|
||||
|
||||
let locationPathname = location.pathname;
|
||||
let toPathname = path.pathname;
|
||||
const isActive = useMemo(() => {
|
||||
if (!to) {
|
||||
return false;
|
||||
}
|
||||
let locationPathName = location.pathname;
|
||||
let toPathName = path.pathname;
|
||||
|
||||
if (!caseSensitive) {
|
||||
locationPathname = locationPathname.toLowerCase();
|
||||
toPathname = toPathname.toLowerCase();
|
||||
locationPathName = locationPathName.toLowerCase();
|
||||
toPathName = toPathName.toLowerCase();
|
||||
}
|
||||
|
||||
return (
|
||||
locationPathname === toPathname ||
|
||||
locationPathName === toPathName ||
|
||||
(!end &&
|
||||
locationPathname.startsWith(toPathname) &&
|
||||
locationPathname.charAt(toPathname.length) === "/")
|
||||
locationPathName.startsWith(toPathName) &&
|
||||
locationPathName.charAt(toPathName.length) === "/")
|
||||
);
|
||||
}, [location, path, caseSensitive, end, to]);
|
||||
|
||||
return isActive;
|
||||
};
|
||||
|
@ -15,11 +15,11 @@
|
||||
// =============================================================================
|
||||
|
||||
import { QueryKeys } from "@constants/query-keys";
|
||||
import { validateSession } from "@src/utils/session";
|
||||
import { validateSession } from "@utils/session";
|
||||
import { useQuery } from "react-query";
|
||||
|
||||
export function useAuth() {
|
||||
return useQuery(
|
||||
return useQuery<boolean, Error>(
|
||||
QueryKeys.AuthSession,
|
||||
async () => {
|
||||
if (await validateSession()) {
|
||||
|
@ -14,10 +14,10 @@
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import { QueryKeys } from "@constants/query-keys";
|
||||
import { getIpcError } from "@src/utils/get-ipc-error";
|
||||
import { createSession } from "@src/utils/session";
|
||||
import { useMutation, useQueryClient } from "react-query";
|
||||
import { QueryKeys } from "@constants/query-keys";
|
||||
import { getIpcError } from "@utils/get-ipc-error";
|
||||
import { createSession } from "@utils/session";
|
||||
|
||||
interface Variables {
|
||||
currentPassword: string;
|
||||
|
@ -17,7 +17,7 @@
|
||||
import { createStyles } from "@mantine/core";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { LangKeys } from "@constants/lang";
|
||||
import { NodeConnectSwitch } from "@atoms/NodeConnectSwitch";
|
||||
import { NodeConnectSwitch } from "@molecules/NodeConnectSwitch";
|
||||
import { ReactComponent as CloudIcon } from "@assets/setting-cloud.svg";
|
||||
import { ReactComponent as ServerIcon } from "@assets/setting-server.svg";
|
||||
import { NodeLocalForm } from "./NodeLocalForm";
|
||||
|
@ -15,20 +15,15 @@
|
||||
// =============================================================================
|
||||
|
||||
import type { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||
import { AppProviders } from "@atoms/AppProviders";
|
||||
import { Home } from ".";
|
||||
|
||||
export default {
|
||||
title: "pages/Onboarding/Home",
|
||||
title: "pages/Home",
|
||||
component: Home,
|
||||
} as ComponentMeta<typeof Home>;
|
||||
|
||||
const Template: ComponentStory<typeof Home> = () => {
|
||||
return (
|
||||
<AppProviders>
|
||||
<Home />
|
||||
</AppProviders>
|
||||
);
|
||||
return <Home />;
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
@ -14,8 +14,17 @@
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import { NavbarLayout } from "@templates/NavbarLayout";
|
||||
import type { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||
import { Login } from ".";
|
||||
|
||||
export function Wallet() {
|
||||
return <NavbarLayout></NavbarLayout>;
|
||||
}
|
||||
export default {
|
||||
title: "pages/Login",
|
||||
component: Login,
|
||||
} as ComponentMeta<typeof Login>;
|
||||
|
||||
const Template: ComponentStory<typeof Login> = () => {
|
||||
return <Login />;
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {};
|
113
packages/renderer/src/pages/Login/Login.test.tsx
Normal file
113
packages/renderer/src/pages/Login/Login.test.tsx
Normal file
@ -0,0 +1,113 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// 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.
|
||||
// =============================================================================
|
||||
|
||||
const navSpy = vi.fn();
|
||||
const loginSpy = vi.fn();
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { AppProviders } from "@atoms/AppProviders";
|
||||
import { Login } from ".";
|
||||
import { ROUTES } from "@constants/routes";
|
||||
|
||||
describe("pages::Login", () => {
|
||||
beforeEach(() => {
|
||||
vi.mock("react-router-dom", async () => ({
|
||||
...(await vi.importActual<any>("react-router-dom")), // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
useNavigate: () => navSpy,
|
||||
}));
|
||||
|
||||
vi.mock("@hooks/session/useLogin", () => ({
|
||||
useLogin: () => ({
|
||||
mutate: loginSpy,
|
||||
isLoading: false,
|
||||
}),
|
||||
}));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("renders without exploding", () => {
|
||||
const { asFragment, unmount } = render(
|
||||
<AppProviders>
|
||||
<Login />
|
||||
</AppProviders>
|
||||
);
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it("blocks login if validation fails", async () => {
|
||||
const user = userEvent.setup();
|
||||
const { unmount } = render(
|
||||
<AppProviders>
|
||||
<Login />
|
||||
</AppProviders>
|
||||
);
|
||||
const btnSubmit = screen.getByRole("button", { name: "Login" });
|
||||
fireEvent.submit(btnSubmit);
|
||||
expect(loginSpy).to.not.toHaveBeenCalled();
|
||||
// try a short password
|
||||
await user.type(screen.getByLabelText("Password"), "foo");
|
||||
fireEvent.submit(btnSubmit);
|
||||
expect(loginSpy).to.not.toHaveBeenCalled();
|
||||
unmount();
|
||||
});
|
||||
|
||||
it("calls login", async () => {
|
||||
const PASSWORD = "Haveno!2022";
|
||||
const user = userEvent.setup();
|
||||
const { unmount } = render(
|
||||
<AppProviders>
|
||||
<Login />
|
||||
</AppProviders>
|
||||
);
|
||||
expect(loginSpy).to.not.toHaveBeenCalled();
|
||||
await user.type(screen.getByLabelText("Password"), PASSWORD);
|
||||
fireEvent.submit(screen.getByRole("button", { name: "Login" }));
|
||||
expect(loginSpy).to.toHaveBeenCalledTimes(1);
|
||||
unmount();
|
||||
});
|
||||
|
||||
// TODO: update behavior to redirect to the Market page
|
||||
it("navigates to Payment Accounts page after successful login", async () => {
|
||||
const PASSWORD = "Haveno!2022";
|
||||
loginSpy.mockImplementation(({ password }, { onSuccess, onError }) => {
|
||||
if (password === PASSWORD) {
|
||||
onSuccess();
|
||||
} else {
|
||||
onError({ message: "Invalid password" });
|
||||
}
|
||||
});
|
||||
const user = userEvent.setup();
|
||||
const { unmount } = render(
|
||||
<AppProviders>
|
||||
<Login />
|
||||
</AppProviders>
|
||||
);
|
||||
expect(navSpy).to.toHaveBeenCalledTimes(0);
|
||||
await user.type(screen.getByLabelText("Password"), PASSWORD);
|
||||
fireEvent.submit(screen.getByRole("button", { name: "Login" }));
|
||||
expect(navSpy).to.toHaveBeenCalledTimes(1);
|
||||
expect(navSpy).toHaveBeenCalledWith(ROUTES.AccountPaymentAccounts, {
|
||||
replace: true,
|
||||
});
|
||||
unmount();
|
||||
});
|
||||
});
|
@ -19,12 +19,12 @@ import { joiResolver, useForm } from "@mantine/form";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Container, Group, Space, Stack } from "@mantine/core";
|
||||
import { showNotification } from "@mantine/notifications";
|
||||
import { useLogin } from "@hooks/session/useLogin";
|
||||
import { CenteredLayout } from "@templates/CenteredLayout";
|
||||
import { BodyText, Heading } from "@atoms/Typography";
|
||||
import { ROUTES } from "@constants/routes";
|
||||
import { useLogin } from "@hooks/session/useLogin";
|
||||
import { Button } from "@atoms/Buttons";
|
||||
import { TextInput } from "@atoms/TextInput";
|
||||
import { ROUTES } from "@constants/routes";
|
||||
import { CONTENT_MAX_WIDTH } from "./_constants";
|
||||
|
||||
export function Login() {
|
||||
@ -67,6 +67,7 @@ export function Login() {
|
||||
</BodyText>
|
||||
<Space h="lg" />
|
||||
<TextInput
|
||||
aria-label="Password"
|
||||
id="password"
|
||||
label="Password"
|
||||
type="password"
|
||||
|
@ -0,0 +1,94 @@
|
||||
// Vitest Snapshot v1
|
||||
|
||||
exports[`pages::Login > renders without exploding 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
class="mantine-Stack-root mantine-14yk9bp"
|
||||
>
|
||||
<header
|
||||
class="mantine-caub1y"
|
||||
>
|
||||
<img
|
||||
alt="Haveno"
|
||||
height="24"
|
||||
src="/assets/logo.svg"
|
||||
/>
|
||||
</header>
|
||||
<div
|
||||
class="mantine-Container-root mantine-1hpmr7z"
|
||||
>
|
||||
<div
|
||||
class="mantine-Stack-root mantine-12lpdrc"
|
||||
>
|
||||
<form>
|
||||
<div
|
||||
class="mantine-Stack-root mantine-lfk3cq"
|
||||
>
|
||||
<div
|
||||
class="mantine-Container-root mantine-me1cdz"
|
||||
>
|
||||
<h1
|
||||
class="mantine-Title-root mantine-1rawl7h"
|
||||
>
|
||||
Login to Haveno
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mantine-Text-root mantine-1ourfup"
|
||||
>
|
||||
All your data is stored locally on your machine. Haveno uses solely a password.
|
||||
</div>
|
||||
<div
|
||||
class="mantine-63n06h"
|
||||
/>
|
||||
<div
|
||||
class="mantine-TextInput-root mantine-14qek68"
|
||||
>
|
||||
<label
|
||||
class="mantine-TextInput-label mantine-1bjo575"
|
||||
for="password"
|
||||
id="password-label"
|
||||
>
|
||||
Password
|
||||
</label>
|
||||
<div
|
||||
class="mantine-TextInput-wrapper mantine-12sbrde"
|
||||
>
|
||||
<input
|
||||
aria-invalid="false"
|
||||
aria-label="Password"
|
||||
class="mantine-TextInput-defaultVariant mantine-TextInput-input mantine-nk8491"
|
||||
id="password"
|
||||
type="password"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mantine-63n06h"
|
||||
/>
|
||||
<div
|
||||
class="mantine-Group-root mantine-19jxmdp"
|
||||
>
|
||||
<button
|
||||
class="mantine-Button-filled mantine-Button-root mantine-Group-child mantine-s2bdxi"
|
||||
type="submit"
|
||||
>
|
||||
<div
|
||||
class="mantine-3xbgk5 mantine-Button-inner"
|
||||
>
|
||||
<span
|
||||
class="mantine-qo1k2 mantine-Button-label"
|
||||
>
|
||||
Login
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DocumentFragment>
|
||||
`;
|
@ -0,0 +1,30 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// 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 type { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||
import { ConnectingMonero } from ".";
|
||||
|
||||
export default {
|
||||
title: "pages/Onboarding/Connecting Monero",
|
||||
component: ConnectingMonero,
|
||||
} as ComponentMeta<typeof ConnectingMonero>;
|
||||
|
||||
const Template: ComponentStory<typeof ConnectingMonero> = () => {
|
||||
return <ConnectingMonero />;
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {};
|
@ -0,0 +1,30 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// 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 type { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||
import { CreateAccount } from ".";
|
||||
|
||||
export default {
|
||||
title: "pages/Onboarding/Create Account",
|
||||
component: CreateAccount,
|
||||
} as ComponentMeta<typeof CreateAccount>;
|
||||
|
||||
const Template: ComponentStory<typeof CreateAccount> = () => {
|
||||
return <CreateAccount />;
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {};
|
@ -15,7 +15,6 @@
|
||||
// =============================================================================
|
||||
|
||||
import type { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||
import { AppProviders } from "@atoms/AppProviders";
|
||||
import { Welcome } from ".";
|
||||
|
||||
export default {
|
||||
@ -24,11 +23,7 @@ export default {
|
||||
} as ComponentMeta<typeof Welcome>;
|
||||
|
||||
const Template: ComponentStory<typeof Welcome> = () => {
|
||||
return (
|
||||
<AppProviders>
|
||||
<Welcome />
|
||||
</AppProviders>
|
||||
);
|
||||
return <Welcome />;
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
|
@ -14,5 +14,6 @@
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
export * from "./Welcome";
|
||||
export * from "./ConnectingMonero";
|
||||
export * from "./CreateAccount";
|
||||
export * from "./Welcome";
|
||||
|
@ -32,7 +32,8 @@
|
||||
"src/**/*.tsx",
|
||||
"types/**/*.d.ts",
|
||||
"../../types/**/*.d.ts",
|
||||
"../preload/contracts.d.ts"
|
||||
"../preload/contracts.d.ts",
|
||||
"../../tests/setup-tests.ts"
|
||||
],
|
||||
"exclude": ["**/*.spec.ts", "**/*.test.ts"]
|
||||
}
|
||||
|
@ -14,19 +14,24 @@
|
||||
// limitations under the License.
|
||||
// =============================================================================
|
||||
|
||||
import { mergeConfig } from "vite";
|
||||
import viteConfig from "./vite.config";
|
||||
|
||||
/**
|
||||
* Config for global end-to-end tests
|
||||
* placed in project root tests folder
|
||||
* @type {import('vite').UserConfig}
|
||||
* @see https://vitest.dev/config/
|
||||
*/
|
||||
const config = {
|
||||
const config = mergeConfig(viteConfig, {
|
||||
test: {
|
||||
setupFiles: ["../../tests/setup-tests.ts"],
|
||||
environment: "jsdom",
|
||||
include: ["./src/**/*.{test,spec}.{ts,tsx}"],
|
||||
coverage: {
|
||||
reporter: ["html"],
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
export default config;
|
30
tests/setup-tests.ts
Normal file
30
tests/setup-tests.ts
Normal file
@ -0,0 +1,30 @@
|
||||
// =============================================================================
|
||||
// Copyright 2022 Haveno
|
||||
//
|
||||
// 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.
|
||||
// =============================================================================
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-namespace,@typescript-eslint/no-explicit-any */
|
||||
import { expect } from "vitest";
|
||||
import type { TestingLibraryMatchers } from "@testing-library/jest-dom/matchers";
|
||||
import matchers from "@testing-library/jest-dom/matchers";
|
||||
|
||||
declare global {
|
||||
namespace Vi {
|
||||
interface JestAssertion<T = any>
|
||||
extends jest.Matchers<void, T>,
|
||||
TestingLibraryMatchers<T, void> {}
|
||||
}
|
||||
}
|
||||
|
||||
expect.extend(matchers);
|
315
yarn.lock
315
yarn.lock
@ -1074,7 +1074,7 @@
|
||||
pirates "^4.0.5"
|
||||
source-map-support "^0.5.16"
|
||||
|
||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.8", "@babel/runtime@^7.17.8", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
|
||||
"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.8", "@babel/runtime@^7.17.8", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
|
||||
version "7.17.9"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72"
|
||||
integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==
|
||||
@ -2291,10 +2291,10 @@
|
||||
ts-dedent "^2.0.0"
|
||||
util-deprecate "^1.0.2"
|
||||
|
||||
"@storybook/builder-vite@^0.1.29":
|
||||
version "0.1.29"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/builder-vite/-/builder-vite-0.1.29.tgz#ca7dff079968605e323b365d72c28903e4a072eb"
|
||||
integrity sha512-WMPY1Pd5Da3BdXDfgFNhIWq09i7oxMT06nxS909VKOOKHZvckmfQpS8iKJLYp730t4t7S3+MtHf/t2+Kr7Cxew==
|
||||
"@storybook/builder-vite@^0.1.34":
|
||||
version "0.1.34"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/builder-vite/-/builder-vite-0.1.34.tgz#9d65a1b430967cae688ec7cb586056e53f1afef2"
|
||||
integrity sha512-a40uMOAu66W4zX9+k+wGhhfsyJWSXb7i4Ve5rS9I1fZ/d7xvsjhgQzhHZtLqESU1A/CO1gyvexbcw44efMPuGQ==
|
||||
dependencies:
|
||||
"@joshwooding/vite-plugin-react-docgen-typescript" "0.0.4"
|
||||
"@mdx-js/mdx" "^1.6.22"
|
||||
@ -3018,6 +3018,21 @@
|
||||
lz-string "^1.4.4"
|
||||
pretty-format "^27.0.2"
|
||||
|
||||
"@testing-library/jest-dom@^5.16.4":
|
||||
version "5.16.4"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.4.tgz#938302d7b8b483963a3ae821f1c0808f872245cd"
|
||||
integrity sha512-Gy+IoFutbMQcky0k+bqqumXZ1cTGswLsFqmNLzNdSKkU9KGV2u9oXhukCbbJ9/LRPKiqwxEE8VpV/+YZlfkPUA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
"@types/testing-library__jest-dom" "^5.9.1"
|
||||
aria-query "^5.0.0"
|
||||
chalk "^3.0.0"
|
||||
css "^3.0.0"
|
||||
css.escape "^1.5.1"
|
||||
dom-accessibility-api "^0.5.6"
|
||||
lodash "^4.17.15"
|
||||
redent "^3.0.0"
|
||||
|
||||
"@testing-library/react@^12":
|
||||
version "12.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.5.tgz#bb248f72f02a5ac9d949dea07279095fa577963b"
|
||||
@ -3032,6 +3047,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.1.1.tgz#e1ff6118896e4b22af31e5ea2f9da956adde23d8"
|
||||
integrity sha512-XrjH/iEUqNl9lF2HX9YhPNV7Amntkcnpw0Bo1KkRzowNDcgSN9i0nm4Q8Oi5wupgdfPaJNMAWa61A+voD6Kmwg==
|
||||
|
||||
"@testing-library/user-event@^14.2.0":
|
||||
version "14.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.2.0.tgz#8293560f8f80a00383d6c755ec3e0b918acb1683"
|
||||
integrity sha512-+hIlG4nJS6ivZrKnOP7OGsDu9Fxmryj9vCl8x0ZINtTJcCHs2zLsYif5GzuRiBF2ck5GZG2aQr7Msg+EHlnYVQ==
|
||||
|
||||
"@tootallnate/once@2":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
|
||||
@ -3173,6 +3193,14 @@
|
||||
dependencies:
|
||||
"@types/istanbul-lib-report" "*"
|
||||
|
||||
"@types/jest@*":
|
||||
version "27.5.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.5.1.tgz#2c8b6dc6ff85c33bcd07d0b62cb3d19ddfdb3ab9"
|
||||
integrity sha512-fUy7YRpT+rHXto1YlL+J9rs0uLGyiqVt3ZOTQR+4ROc47yNl8WLdVLgUloBRhOxP1PZvguHl44T3H0wAWxahYQ==
|
||||
dependencies:
|
||||
jest-matcher-utils "^27.0.0"
|
||||
pretty-format "^27.0.0"
|
||||
|
||||
"@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
|
||||
version "7.0.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
|
||||
@ -3360,6 +3388,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310"
|
||||
integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==
|
||||
|
||||
"@types/testing-library__jest-dom@^5.9.1":
|
||||
version "5.14.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.3.tgz#ee6c7ffe9f8595882ee7bda8af33ae7b8789ef17"
|
||||
integrity sha512-oKZe+Mf4ioWlMuzVBaXQ9WDnEm1+umLx0InILg+yvZVBBDmzV5KfZyLrCvadtWcx8+916jLmHafcmqqffl+iIw==
|
||||
dependencies:
|
||||
"@types/jest" "*"
|
||||
|
||||
"@types/uglify-js@*":
|
||||
version "3.13.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.13.2.tgz#1044c1713fb81cb1ceef29ad8a9ee1ce08d690ef"
|
||||
@ -3741,6 +3776,11 @@ JSONStream@^1.0.4:
|
||||
jsonparse "^1.2.0"
|
||||
through ">=2.2.7 <3"
|
||||
|
||||
abab@^2.0.5, abab@^2.0.6:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291"
|
||||
integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==
|
||||
|
||||
accepts@~1.3.5, accepts@~1.3.8:
|
||||
version "1.3.8"
|
||||
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
|
||||
@ -3749,12 +3789,20 @@ accepts@~1.3.5, accepts@~1.3.8:
|
||||
mime-types "~2.1.34"
|
||||
negotiator "0.6.3"
|
||||
|
||||
acorn-globals@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45"
|
||||
integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==
|
||||
dependencies:
|
||||
acorn "^7.1.1"
|
||||
acorn-walk "^7.1.1"
|
||||
|
||||
acorn-jsx@^5.3.1:
|
||||
version "5.3.2"
|
||||
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
|
||||
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
||||
|
||||
acorn-walk@^7.2.0:
|
||||
acorn-walk@^7.1.1, acorn-walk@^7.2.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
|
||||
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
|
||||
@ -3769,7 +3817,7 @@ acorn@^6.4.1:
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6"
|
||||
integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==
|
||||
|
||||
acorn@^7.4.1:
|
||||
acorn@^7.1.1, acorn@^7.4.1:
|
||||
version "7.4.1"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
|
||||
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
||||
@ -4562,6 +4610,11 @@ brorand@^1.0.1, brorand@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
|
||||
integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
|
||||
|
||||
browser-process-hrtime@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626"
|
||||
integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==
|
||||
|
||||
browserify-aes@^1.0.0, browserify-aes@^1.0.4:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48"
|
||||
@ -4952,6 +5005,14 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.1:
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
chalk@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
|
||||
integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
|
||||
dependencies:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
||||
@ -5686,11 +5747,37 @@ css.escape@^1.5.1:
|
||||
resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
|
||||
integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=
|
||||
|
||||
css@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d"
|
||||
integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==
|
||||
dependencies:
|
||||
inherits "^2.0.4"
|
||||
source-map "^0.6.1"
|
||||
source-map-resolve "^0.6.0"
|
||||
|
||||
cssesc@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
|
||||
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
|
||||
|
||||
cssom@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36"
|
||||
integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==
|
||||
|
||||
cssom@~0.3.6:
|
||||
version "0.3.8"
|
||||
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a"
|
||||
integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==
|
||||
|
||||
cssstyle@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852"
|
||||
integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==
|
||||
dependencies:
|
||||
cssom "~0.3.6"
|
||||
|
||||
csstype@^2.5.7:
|
||||
version "2.6.20"
|
||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.20.tgz#9229c65ea0b260cf4d3d997cb06288e36a8d6dda"
|
||||
@ -5711,6 +5798,15 @@ dargs@^7.0.0:
|
||||
resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc"
|
||||
integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==
|
||||
|
||||
data-urls@^3.0.1:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143"
|
||||
integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==
|
||||
dependencies:
|
||||
abab "^2.0.6"
|
||||
whatwg-mimetype "^3.0.0"
|
||||
whatwg-url "^11.0.0"
|
||||
|
||||
dayjs@^1.11.0:
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.0.tgz#009bf7ef2e2ea2d5db2e6583d2d39a4b5061e805"
|
||||
@ -5764,6 +5860,11 @@ decamelize@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
|
||||
|
||||
decimal.js@^10.3.1:
|
||||
version "10.3.1"
|
||||
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783"
|
||||
integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==
|
||||
|
||||
decode-uri-component@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
|
||||
@ -5898,6 +5999,11 @@ detect-port@^1.3.0:
|
||||
address "^1.0.1"
|
||||
debug "^2.6.0"
|
||||
|
||||
diff-sequences@^27.5.1:
|
||||
version "27.5.1"
|
||||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327"
|
||||
integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==
|
||||
|
||||
diff@^4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
|
||||
@ -5978,6 +6084,11 @@ doctrine@^3.0.0:
|
||||
dependencies:
|
||||
esutils "^2.0.2"
|
||||
|
||||
dom-accessibility-api@^0.5.6:
|
||||
version "0.5.14"
|
||||
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz#56082f71b1dc7aac69d83c4285eef39c15d93f56"
|
||||
integrity sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg==
|
||||
|
||||
dom-accessibility-api@^0.5.9:
|
||||
version "0.5.13"
|
||||
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.13.tgz#102ee5f25eacce09bdf1cfa5a298f86da473be4b"
|
||||
@ -6022,6 +6133,13 @@ domelementtype@^2.0.1, domelementtype@^2.2.0:
|
||||
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
|
||||
integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
|
||||
|
||||
domexception@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673"
|
||||
integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==
|
||||
dependencies:
|
||||
webidl-conversions "^7.0.0"
|
||||
|
||||
domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c"
|
||||
@ -8035,6 +8153,13 @@ hosted-git-info@^4.0.1, hosted-git-info@^4.0.2:
|
||||
dependencies:
|
||||
lru-cache "^6.0.0"
|
||||
|
||||
html-encoding-sniffer@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9"
|
||||
integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==
|
||||
dependencies:
|
||||
whatwg-encoding "^2.0.0"
|
||||
|
||||
html-entities@^2.1.0:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.3.tgz#117d7626bece327fc8baace8868fa6f5ef856e46"
|
||||
@ -8173,7 +8298,7 @@ iconv-lite@0.4.24:
|
||||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3"
|
||||
|
||||
iconv-lite@^0.6.2:
|
||||
iconv-lite@0.6.3, iconv-lite@^0.6.2:
|
||||
version "0.6.3"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
|
||||
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
|
||||
@ -8655,6 +8780,11 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4:
|
||||
dependencies:
|
||||
isobject "^3.0.1"
|
||||
|
||||
is-potential-custom-element-name@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5"
|
||||
integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==
|
||||
|
||||
is-redirect@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24"
|
||||
@ -8865,6 +8995,21 @@ jake@^10.8.5:
|
||||
filelist "^1.0.1"
|
||||
minimatch "^3.0.4"
|
||||
|
||||
jest-diff@^27.5.1:
|
||||
version "27.5.1"
|
||||
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def"
|
||||
integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==
|
||||
dependencies:
|
||||
chalk "^4.0.0"
|
||||
diff-sequences "^27.5.1"
|
||||
jest-get-type "^27.5.1"
|
||||
pretty-format "^27.5.1"
|
||||
|
||||
jest-get-type@^27.5.1:
|
||||
version "27.5.1"
|
||||
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1"
|
||||
integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==
|
||||
|
||||
jest-haste-map@^26.6.2:
|
||||
version "26.6.2"
|
||||
resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa"
|
||||
@ -8886,6 +9031,16 @@ jest-haste-map@^26.6.2:
|
||||
optionalDependencies:
|
||||
fsevents "^2.1.2"
|
||||
|
||||
jest-matcher-utils@^27.0.0:
|
||||
version "27.5.1"
|
||||
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab"
|
||||
integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==
|
||||
dependencies:
|
||||
chalk "^4.0.0"
|
||||
jest-diff "^27.5.1"
|
||||
jest-get-type "^27.5.1"
|
||||
pretty-format "^27.5.1"
|
||||
|
||||
jest-mock@^27.0.6:
|
||||
version "27.5.1"
|
||||
resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6"
|
||||
@ -8974,6 +9129,39 @@ js-yaml@^4.1.0:
|
||||
dependencies:
|
||||
argparse "^2.0.1"
|
||||
|
||||
jsdom@^19.0.0:
|
||||
version "19.0.0"
|
||||
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-19.0.0.tgz#93e67c149fe26816d38a849ea30ac93677e16b6a"
|
||||
integrity sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A==
|
||||
dependencies:
|
||||
abab "^2.0.5"
|
||||
acorn "^8.5.0"
|
||||
acorn-globals "^6.0.0"
|
||||
cssom "^0.5.0"
|
||||
cssstyle "^2.3.0"
|
||||
data-urls "^3.0.1"
|
||||
decimal.js "^10.3.1"
|
||||
domexception "^4.0.0"
|
||||
escodegen "^2.0.0"
|
||||
form-data "^4.0.0"
|
||||
html-encoding-sniffer "^3.0.0"
|
||||
http-proxy-agent "^5.0.0"
|
||||
https-proxy-agent "^5.0.0"
|
||||
is-potential-custom-element-name "^1.0.1"
|
||||
nwsapi "^2.2.0"
|
||||
parse5 "6.0.1"
|
||||
saxes "^5.0.1"
|
||||
symbol-tree "^3.2.4"
|
||||
tough-cookie "^4.0.0"
|
||||
w3c-hr-time "^1.0.2"
|
||||
w3c-xmlserializer "^3.0.0"
|
||||
webidl-conversions "^7.0.0"
|
||||
whatwg-encoding "^2.0.0"
|
||||
whatwg-mimetype "^3.0.0"
|
||||
whatwg-url "^10.0.0"
|
||||
ws "^8.2.3"
|
||||
xml-name-validator "^4.0.0"
|
||||
|
||||
jsesc@^2.5.1:
|
||||
version "2.5.2"
|
||||
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
|
||||
@ -10104,6 +10292,11 @@ num2fraction@^1.2.2:
|
||||
resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
|
||||
integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=
|
||||
|
||||
nwsapi@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7"
|
||||
integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==
|
||||
|
||||
object-assign@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
@ -10458,7 +10651,7 @@ parse-json@^5.0.0:
|
||||
json-parse-even-better-errors "^2.3.0"
|
||||
lines-and-columns "^1.1.6"
|
||||
|
||||
parse5@^6.0.0:
|
||||
parse5@6.0.1, parse5@^6.0.0:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
|
||||
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
|
||||
@ -10809,7 +11002,7 @@ pretty-error@^2.1.1:
|
||||
lodash "^4.17.20"
|
||||
renderkid "^2.0.4"
|
||||
|
||||
pretty-format@^27.0.2:
|
||||
pretty-format@^27.0.0, pretty-format@^27.0.2, pretty-format@^27.5.1:
|
||||
version "27.5.1"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e"
|
||||
integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==
|
||||
@ -10942,6 +11135,11 @@ pseudomap@^1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
|
||||
integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
|
||||
|
||||
psl@^1.1.33:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
|
||||
integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==
|
||||
|
||||
public-encrypt@^4.0.0:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0"
|
||||
@ -10989,7 +11187,7 @@ punycode@^1.2.4:
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
|
||||
integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
|
||||
|
||||
punycode@^2.1.0:
|
||||
punycode@^2.1.0, punycode@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||
@ -11814,6 +12012,13 @@ sax@^1.2.4:
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
||||
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
|
||||
|
||||
saxes@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d"
|
||||
integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==
|
||||
dependencies:
|
||||
xmlchars "^2.2.0"
|
||||
|
||||
scheduler@^0.20.2:
|
||||
version "0.20.2"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
|
||||
@ -12157,6 +12362,14 @@ source-map-resolve@^0.5.0:
|
||||
source-map-url "^0.4.0"
|
||||
urix "^0.1.0"
|
||||
|
||||
source-map-resolve@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2"
|
||||
integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==
|
||||
dependencies:
|
||||
atob "^2.1.2"
|
||||
decode-uri-component "^0.2.0"
|
||||
|
||||
source-map-support@^0.5.16, source-map-support@^0.5.19, source-map-support@~0.5.12, source-map-support@~0.5.20:
|
||||
version "0.5.21"
|
||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
|
||||
@ -12514,6 +12727,11 @@ svg-parser@^2.0.2:
|
||||
resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5"
|
||||
integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==
|
||||
|
||||
symbol-tree@^3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
|
||||
integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
|
||||
|
||||
symbol.prototype.description@^1.0.0:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/symbol.prototype.description/-/symbol.prototype.description-1.0.5.tgz#d30e01263b6020fbbd2d2884a6276ce4d49ab568"
|
||||
@ -12545,6 +12763,11 @@ synchronous-promise@^2.0.15:
|
||||
resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.15.tgz#07ca1822b9de0001f5ff73595f3d08c4f720eb8e"
|
||||
integrity sha512-k8uzYIkIVwmT+TcglpdN50pS2y1BDcUnBPK9iJeGu0Pl1lOI8pD6wtzgw91Pjpe+RxtTncw32tLxs/R0yNL2Mg==
|
||||
|
||||
tabler-icons-react@^1.48.0:
|
||||
version "1.48.0"
|
||||
resolved "https://registry.yarnpkg.com/tabler-icons-react/-/tabler-icons-react-1.48.0.tgz#2b3251d4b9effa1e78baf4cb05fe7cf79449f116"
|
||||
integrity sha512-dlcAIGYIB7+fsU1tj8HuK5aN57g3Q5KD8GMAxpBR9E62yFhjn8fbwD2M2X18E66v5TYakUhu6tfUxM+/jvI6Kg==
|
||||
|
||||
tapable@^1.0.0, tapable@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
|
||||
@ -12799,6 +13022,22 @@ toidentifier@1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
|
||||
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
|
||||
|
||||
tough-cookie@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4"
|
||||
integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==
|
||||
dependencies:
|
||||
psl "^1.1.33"
|
||||
punycode "^2.1.1"
|
||||
universalify "^0.1.2"
|
||||
|
||||
tr46@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9"
|
||||
integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==
|
||||
dependencies:
|
||||
punycode "^2.1.1"
|
||||
|
||||
tr46@~0.0.3:
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
|
||||
@ -13137,7 +13376,7 @@ unist-util-visit@2.0.3, unist-util-visit@^2.0.0:
|
||||
unist-util-is "^4.0.0"
|
||||
unist-util-visit-parents "^3.0.0"
|
||||
|
||||
universalify@^0.1.0:
|
||||
universalify@^0.1.0, universalify@^0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
|
||||
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
|
||||
@ -13463,6 +13702,20 @@ vm-browserify@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
|
||||
integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==
|
||||
|
||||
w3c-hr-time@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd"
|
||||
integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==
|
||||
dependencies:
|
||||
browser-process-hrtime "^1.0.0"
|
||||
|
||||
w3c-xmlserializer@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz#06cdc3eefb7e4d0b20a560a5a3aeb0d2d9a65923"
|
||||
integrity sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==
|
||||
dependencies:
|
||||
xml-name-validator "^4.0.0"
|
||||
|
||||
walker@^1.0.7, walker@~1.0.5:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f"
|
||||
@ -13603,11 +13856,39 @@ whatwg-encoding@^1.0.5:
|
||||
dependencies:
|
||||
iconv-lite "0.4.24"
|
||||
|
||||
whatwg-encoding@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53"
|
||||
integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==
|
||||
dependencies:
|
||||
iconv-lite "0.6.3"
|
||||
|
||||
whatwg-mimetype@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
|
||||
integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
|
||||
|
||||
whatwg-mimetype@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7"
|
||||
integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==
|
||||
|
||||
whatwg-url@^10.0.0:
|
||||
version "10.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-10.0.0.tgz#37264f720b575b4a311bd4094ed8c760caaa05da"
|
||||
integrity sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==
|
||||
dependencies:
|
||||
tr46 "^3.0.0"
|
||||
webidl-conversions "^7.0.0"
|
||||
|
||||
whatwg-url@^11.0.0:
|
||||
version "11.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018"
|
||||
integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==
|
||||
dependencies:
|
||||
tr46 "^3.0.0"
|
||||
webidl-conversions "^7.0.0"
|
||||
|
||||
whatwg-url@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
|
||||
@ -13744,6 +14025,11 @@ xdg-basedir@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"
|
||||
integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==
|
||||
|
||||
xml-name-validator@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835"
|
||||
integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==
|
||||
|
||||
xmlbuilder@>=11.0.1:
|
||||
version "15.1.1"
|
||||
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5"
|
||||
@ -13754,6 +14040,11 @@ xmlbuilder@^9.0.7:
|
||||
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
|
||||
integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
|
||||
|
||||
xmlchars@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
|
||||
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
|
||||
|
||||
xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
|
||||
|
Loading…
Reference in New Issue
Block a user