mirror of
https://github.com/ansible/awx.git
synced 2026-01-12 18:40:01 -03:30
Show token/refresh token/expires in modal after successful token add
This commit is contained in:
parent
957ab9bf7c
commit
920eda9999
@ -14,7 +14,6 @@ import { Card, PageSection } from '@patternfly/react-core';
|
||||
import useRequest from '../../util/useRequest';
|
||||
import { UsersAPI } from '../../api';
|
||||
import ContentError from '../../components/ContentError';
|
||||
import ContentLoading from '../../components/ContentLoading';
|
||||
import RoutedTabs from '../../components/RoutedTabs';
|
||||
import UserDetail from './UserDetail';
|
||||
import UserEdit from './UserEdit';
|
||||
@ -86,7 +85,7 @@ function User({ i18n, setBreadcrumb, me }) {
|
||||
showCardHeader = false;
|
||||
}
|
||||
|
||||
if (contentError) {
|
||||
if (!isLoading && contentError) {
|
||||
return (
|
||||
<PageSection>
|
||||
<Card>
|
||||
@ -107,41 +106,34 @@ function User({ i18n, setBreadcrumb, me }) {
|
||||
<PageSection>
|
||||
<Card>
|
||||
{showCardHeader && <RoutedTabs tabsArray={tabsArray} />}
|
||||
{isLoading && <ContentLoading />}
|
||||
{!isLoading && user && (
|
||||
<Switch>
|
||||
<Redirect from="/users/:id" to="/users/:id/details" exact />
|
||||
{user && (
|
||||
<Route path="/users/:id/edit">
|
||||
<UserEdit user={user} />
|
||||
</Route>
|
||||
)}
|
||||
{user && (
|
||||
<Route path="/users/:id/details">
|
||||
<UserDetail user={user} />
|
||||
</Route>
|
||||
)}
|
||||
<Route path="/users/:id/organizations">
|
||||
<Switch>
|
||||
<Redirect from="/users/:id" to="/users/:id/details" exact />
|
||||
{user && [
|
||||
<Route path="/users/:id/edit" key="edit">
|
||||
<UserEdit user={user} />
|
||||
</Route>,
|
||||
<Route path="/users/:id/details" key="details">
|
||||
<UserDetail user={user} />
|
||||
</Route>,
|
||||
<Route path="/users/:id/organizations" key="organizations">
|
||||
<UserOrganizations id={Number(match.params.id)} />
|
||||
</Route>
|
||||
{user && (
|
||||
<Route path="/users/:id/teams">
|
||||
<UserTeams />
|
||||
</Route>
|
||||
)}
|
||||
{user && (
|
||||
<Route path="/users/:id/roles">
|
||||
<UserRolesList user={user} />
|
||||
</Route>
|
||||
)}
|
||||
<Route path="/users/:id/tokens">
|
||||
</Route>,
|
||||
<Route path="/users/:id/teams" key="teams">
|
||||
<UserTeams />
|
||||
</Route>,
|
||||
<Route path="/users/:id/roles" key="roles">
|
||||
<UserRolesList user={user} />
|
||||
</Route>,
|
||||
<Route path="/users/:id/tokens" key="tokens">
|
||||
<UserTokens
|
||||
user={user}
|
||||
setBreadcrumb={setBreadcrumb}
|
||||
id={Number(match.params.id)}
|
||||
/>
|
||||
</Route>
|
||||
<Route key="not-found" path="*">
|
||||
</Route>,
|
||||
]}
|
||||
<Route key="not-found" path="*">
|
||||
{!isLoading && (
|
||||
<ContentError isNotFound>
|
||||
{match.params.id && (
|
||||
<Link to={`/users/${match.params.id}/details`}>
|
||||
@ -149,9 +141,9 @@ function User({ i18n, setBreadcrumb, me }) {
|
||||
</Link>
|
||||
)}
|
||||
</ContentError>
|
||||
</Route>
|
||||
</Switch>
|
||||
)}
|
||||
)}
|
||||
</Route>
|
||||
</Switch>
|
||||
</Card>
|
||||
</PageSection>
|
||||
);
|
||||
|
||||
@ -22,10 +22,11 @@ async function getUsers() {
|
||||
};
|
||||
}
|
||||
|
||||
UsersAPI.readDetail.mockResolvedValue({ data: mockDetails });
|
||||
UsersAPI.read.mockImplementation(getUsers);
|
||||
|
||||
describe('<User />', () => {
|
||||
test('initially renders successfully', async () => {
|
||||
UsersAPI.readDetail.mockResolvedValue({ data: mockDetails });
|
||||
UsersAPI.read.mockImplementation(getUsers);
|
||||
const history = createMemoryHistory({
|
||||
initialEntries: ['/users/1'],
|
||||
});
|
||||
@ -49,8 +50,6 @@ describe('<User />', () => {
|
||||
});
|
||||
|
||||
test('tabs shown for users', async () => {
|
||||
UsersAPI.readDetail.mockResolvedValue({ data: mockDetails });
|
||||
UsersAPI.read.mockImplementation(getUsers);
|
||||
const history = createMemoryHistory({
|
||||
initialEntries: ['/users/1'],
|
||||
});
|
||||
@ -81,9 +80,7 @@ describe('<User />', () => {
|
||||
expect(wrapper.find('Tabs TabButton').length).toEqual(6);
|
||||
});
|
||||
|
||||
test('should not now Tokens tab', async () => {
|
||||
UsersAPI.readDetail.mockResolvedValue({ data: mockDetails });
|
||||
UsersAPI.read.mockImplementation(getUsers);
|
||||
test('should not show Tokens tab', async () => {
|
||||
const history = createMemoryHistory({
|
||||
initialEntries: ['/users/1'],
|
||||
});
|
||||
|
||||
@ -6,22 +6,27 @@ import { TokensAPI, UsersAPI } from '../../../api';
|
||||
import useRequest from '../../../util/useRequest';
|
||||
import UserTokenFrom from '../shared/UserTokenForm';
|
||||
|
||||
function UserTokenAdd() {
|
||||
function UserTokenAdd({ onSuccessfulAdd }) {
|
||||
const history = useHistory();
|
||||
const { id: userId } = useParams();
|
||||
const { error: submitError, request: handleSubmit } = useRequest(
|
||||
useCallback(
|
||||
async formData => {
|
||||
let response;
|
||||
if (formData.application) {
|
||||
formData.application = formData.application?.id || null;
|
||||
await UsersAPI.createToken(userId, formData);
|
||||
response = await UsersAPI.createToken(userId, {
|
||||
...formData,
|
||||
application: formData.application?.id || null,
|
||||
});
|
||||
} else {
|
||||
await TokensAPI.create(formData);
|
||||
response = await TokensAPI.create(formData);
|
||||
}
|
||||
|
||||
history.push(`/users/${userId}/tokens`);
|
||||
onSuccessfulAdd(response.data);
|
||||
|
||||
history.push(`/users/${userId}/tokens/${response.data.id}/details`);
|
||||
},
|
||||
[history, userId]
|
||||
[history, userId, onSuccessfulAdd]
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@ -18,29 +18,46 @@ jest.mock('react-router-dom', () => ({
|
||||
}));
|
||||
let wrapper;
|
||||
|
||||
const onSuccessfulAdd = jest.fn();
|
||||
|
||||
describe('<UserTokenAdd />', () => {
|
||||
afterEach(() => {
|
||||
wrapper.unmount();
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
test('handleSubmit should post to api', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<UserTokenAdd />);
|
||||
wrapper = mountWithContexts(
|
||||
<UserTokenAdd onSuccessfulAdd={onSuccessfulAdd} />
|
||||
);
|
||||
});
|
||||
UsersAPI.createToken.mockResolvedValueOnce({ data: { id: 1 } });
|
||||
const tokenData = {
|
||||
application: 1,
|
||||
application: {
|
||||
id: 1,
|
||||
},
|
||||
description: 'foo',
|
||||
scope: 'read',
|
||||
};
|
||||
await act(async () => {
|
||||
wrapper.find('UserTokenForm').prop('handleSubmit')(tokenData);
|
||||
});
|
||||
expect(UsersAPI.createToken).toHaveBeenCalledWith(1, tokenData);
|
||||
expect(UsersAPI.createToken).toHaveBeenCalledWith(1, {
|
||||
application: 1,
|
||||
description: 'foo',
|
||||
scope: 'read',
|
||||
});
|
||||
});
|
||||
|
||||
test('should navigate to tokens list when cancel is clicked', async () => {
|
||||
const history = createMemoryHistory({});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<UserTokenAdd />, {
|
||||
context: { router: { history } },
|
||||
});
|
||||
wrapper = mountWithContexts(
|
||||
<UserTokenAdd onSuccessfulAdd={onSuccessfulAdd} />,
|
||||
{
|
||||
context: { router: { history } },
|
||||
}
|
||||
);
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper.find('button[aria-label="Cancel"]').prop('onClick')();
|
||||
@ -48,51 +65,67 @@ describe('<UserTokenAdd />', () => {
|
||||
expect(history.location.pathname).toEqual('/users/1/tokens');
|
||||
});
|
||||
|
||||
test('successful form submission should trigger redirect', async () => {
|
||||
test('successful form submission with application', async () => {
|
||||
const history = createMemoryHistory({});
|
||||
const tokenData = {
|
||||
application: 1,
|
||||
application: {
|
||||
id: 1,
|
||||
},
|
||||
description: 'foo',
|
||||
scope: 'read',
|
||||
};
|
||||
const rtnData = {
|
||||
id: 2,
|
||||
token: 'abc',
|
||||
refresh_token: 'def',
|
||||
expires: '3020-03-28T14:26:48.099297Z',
|
||||
};
|
||||
UsersAPI.createToken.mockResolvedValueOnce({
|
||||
data: {
|
||||
id: 2,
|
||||
...tokenData,
|
||||
},
|
||||
data: rtnData,
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<UserTokenAdd />, {
|
||||
context: { router: { history } },
|
||||
});
|
||||
wrapper = mountWithContexts(
|
||||
<UserTokenAdd onSuccessfulAdd={onSuccessfulAdd} />,
|
||||
{
|
||||
context: { router: { history } },
|
||||
}
|
||||
);
|
||||
});
|
||||
await waitForElement(wrapper, 'button[aria-label="Save"]');
|
||||
await act(async () => {
|
||||
wrapper.find('UserTokenForm').prop('handleSubmit')(tokenData);
|
||||
});
|
||||
expect(history.location.pathname).toEqual('/users/1/tokens');
|
||||
expect(history.location.pathname).toEqual('/users/1/tokens/2/details');
|
||||
expect(onSuccessfulAdd).toHaveBeenCalledWith(rtnData);
|
||||
});
|
||||
|
||||
test('should successful submit form with application', async () => {
|
||||
test('successful form submission without application', async () => {
|
||||
const history = createMemoryHistory({});
|
||||
const tokenData = {
|
||||
scope: 'read',
|
||||
};
|
||||
const rtnData = {
|
||||
id: 2,
|
||||
token: 'abc',
|
||||
refresh_token: null,
|
||||
expires: '3020-03-28T14:26:48.099297Z',
|
||||
};
|
||||
TokensAPI.create.mockResolvedValueOnce({
|
||||
data: {
|
||||
id: 2,
|
||||
...tokenData,
|
||||
},
|
||||
data: rtnData,
|
||||
});
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(<UserTokenAdd />, {
|
||||
context: { router: { history } },
|
||||
});
|
||||
wrapper = mountWithContexts(
|
||||
<UserTokenAdd onSuccessfulAdd={onSuccessfulAdd} />,
|
||||
{
|
||||
context: { router: { history } },
|
||||
}
|
||||
);
|
||||
});
|
||||
await waitForElement(wrapper, 'button[aria-label="Save"]');
|
||||
await act(async () => {
|
||||
wrapper.find('UserTokenForm').prop('handleSubmit')(tokenData);
|
||||
});
|
||||
expect(history.location.pathname).toEqual('/users/1/tokens');
|
||||
expect(history.location.pathname).toEqual('/users/1/tokens/2/details');
|
||||
expect(onSuccessfulAdd).toHaveBeenCalledWith(rtnData);
|
||||
});
|
||||
});
|
||||
|
||||
@ -14,11 +14,19 @@ import {
|
||||
} from '../../../components/DetailList';
|
||||
import ErrorDetail from '../../../components/ErrorDetail';
|
||||
import { TokensAPI } from '../../../api';
|
||||
import { formatDateString } from '../../../util/dates';
|
||||
import useRequest, { useDismissableError } from '../../../util/useRequest';
|
||||
import { toTitleCase } from '../../../util/strings';
|
||||
|
||||
function UserTokenDetail({ token, canEditOrDelete, i18n }) {
|
||||
const { scope, description, created, modified, summary_fields } = token;
|
||||
const {
|
||||
scope,
|
||||
description,
|
||||
created,
|
||||
modified,
|
||||
expires,
|
||||
summary_fields,
|
||||
} = token;
|
||||
const history = useHistory();
|
||||
const { id, tokenId } = useParams();
|
||||
const { request: deleteToken, isLoading, error: deleteError } = useRequest(
|
||||
@ -39,6 +47,7 @@ function UserTokenDetail({ token, canEditOrDelete, i18n }) {
|
||||
/>
|
||||
<Detail label={i18n._(t`Description`)} value={description} />
|
||||
<Detail label={i18n._(t`Scope`)} value={toTitleCase(scope)} />
|
||||
<Detail label={i18n._(t`Expires`)} value={formatDateString(expires)} />
|
||||
<UserDateDetail
|
||||
label={i18n._(t`Created`)}
|
||||
date={created}
|
||||
|
||||
@ -36,28 +36,33 @@ function UserTokenListItem({ i18n, token, isSelected, onSelect }) {
|
||||
/>
|
||||
<DataListItemCells
|
||||
dataListCells={[
|
||||
<DataListCell aria-label={i18n._(t`Token type`)} key="type">
|
||||
<Link to={`/users/${id}/tokens/${token.id}/details`}>
|
||||
{token.summary_fields?.application
|
||||
? i18n._(t`Application access token`)
|
||||
: i18n._(t`Personal access token`)}
|
||||
</Link>
|
||||
</DataListCell>,
|
||||
<DataListCell
|
||||
aria-label={i18n._(t`application name`)}
|
||||
key={token.id}
|
||||
aria-label={i18n._(t`Application name`)}
|
||||
key="applicationName"
|
||||
>
|
||||
{token.summary_fields?.application?.name ? (
|
||||
{token.summary_fields?.application && (
|
||||
<span>
|
||||
<NameLabel>{i18n._(t`Application`)}</NameLabel>
|
||||
<Link to={`/users/${id}/tokens/${token.id}/details`}>
|
||||
<Link
|
||||
to={`/applications/${token.summary_fields.application.id}/details`}
|
||||
>
|
||||
{token.summary_fields.application.name}
|
||||
</Link>
|
||||
</span>
|
||||
) : (
|
||||
<Link to={`/users/${id}/tokens/${token.id}/details`}>
|
||||
{i18n._(t`Personal access token`)}
|
||||
</Link>
|
||||
)}
|
||||
</DataListCell>,
|
||||
<DataListCell aria-label={i18n._(t`scope`)} key={token.scope}>
|
||||
<DataListCell aria-label={i18n._(t`Scope`)} key="scope">
|
||||
<Label>{i18n._(t`Scope`)}</Label>
|
||||
{toTitleCase(token.scope)}
|
||||
</DataListCell>,
|
||||
<DataListCell aria-label={i18n._(t`expiration`)} key="expiration">
|
||||
<DataListCell aria-label={i18n._(t`Expiration`)} key="expiration">
|
||||
<Label>{i18n._(t`Expires`)}</Label>
|
||||
{formatDateString(token.expires)}
|
||||
</DataListCell>,
|
||||
|
||||
@ -21,7 +21,7 @@ const token = {
|
||||
},
|
||||
application: {
|
||||
id: 1,
|
||||
name: 'app',
|
||||
name: 'Foobar app',
|
||||
},
|
||||
},
|
||||
created: '2020-06-23T15:06:43.188634Z',
|
||||
@ -44,22 +44,57 @@ describe('<UserTokenListItem />', () => {
|
||||
expect(wrapper.find('UserTokenListItem').length).toBe(1);
|
||||
});
|
||||
|
||||
test('should render proper data', async () => {
|
||||
test('should render application access token row properly', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<UserTokenListItem isSelected={false} token={token} />
|
||||
);
|
||||
});
|
||||
expect(wrapper.find('DataListCheck').prop('checked')).toBe(false);
|
||||
expect(wrapper.find('PFDataListCell[aria-label="Token type"]').text()).toBe(
|
||||
'Application access token'
|
||||
);
|
||||
expect(
|
||||
wrapper.find('PFDataListCell[aria-label="application name"]').text()
|
||||
).toBe('Applicationapp');
|
||||
expect(wrapper.find('PFDataListCell[aria-label="scope"]').text()).toBe(
|
||||
'ScopeRead'
|
||||
wrapper.find('PFDataListCell[aria-label="Application name"]').text()
|
||||
).toContain('Foobar app');
|
||||
expect(wrapper.find('PFDataListCell[aria-label="Scope"]').text()).toContain(
|
||||
'Read'
|
||||
);
|
||||
expect(wrapper.find('PFDataListCell[aria-label="expiration"]').text()).toBe(
|
||||
'Expires10/25/3019, 3:06:43 PM'
|
||||
expect(
|
||||
wrapper.find('PFDataListCell[aria-label="Expiration"]').text()
|
||||
).toContain('10/25/3019, 3:06:43 PM');
|
||||
});
|
||||
|
||||
test('should render personal access token row properly', async () => {
|
||||
await act(async () => {
|
||||
wrapper = mountWithContexts(
|
||||
<UserTokenListItem
|
||||
isSelected={false}
|
||||
token={{
|
||||
...token,
|
||||
refresh_token: null,
|
||||
application: null,
|
||||
scope: 'write',
|
||||
summary_fields: {
|
||||
user: token.summary_fields.user,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
});
|
||||
expect(wrapper.find('DataListCheck').prop('checked')).toBe(false);
|
||||
expect(wrapper.find('PFDataListCell[aria-label="Token type"]').text()).toBe(
|
||||
'Personal access token'
|
||||
);
|
||||
expect(
|
||||
wrapper.find('PFDataListCell[aria-label="Application name"]').text()
|
||||
).toBe('');
|
||||
expect(wrapper.find('PFDataListCell[aria-label="Scope"]').text()).toContain(
|
||||
'Write'
|
||||
);
|
||||
expect(
|
||||
wrapper.find('PFDataListCell[aria-label="Expiration"]').text()
|
||||
).toContain('10/25/3019, 3:06:43 PM');
|
||||
});
|
||||
|
||||
test('should be checked', async () => {
|
||||
|
||||
@ -1,24 +1,86 @@
|
||||
import React from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { t } from '@lingui/macro';
|
||||
import { Switch, Route, useParams } from 'react-router-dom';
|
||||
import {
|
||||
ClipboardCopy,
|
||||
ClipboardCopyVariant,
|
||||
Modal,
|
||||
} from '@patternfly/react-core';
|
||||
import { formatDateString } from '../../../util/dates';
|
||||
import { Detail, DetailList } from '../../../components/DetailList';
|
||||
import UserTokenAdd from '../UserTokenAdd';
|
||||
import UserTokenList from '../UserTokenList';
|
||||
import UserToken from '../UserToken';
|
||||
|
||||
function UserTokens({ setBreadcrumb, user }) {
|
||||
function UserTokens({ i18n, setBreadcrumb, user }) {
|
||||
const [tokenModalSource, setTokenModalSource] = useState(null);
|
||||
const { id } = useParams();
|
||||
|
||||
const onSuccessfulAdd = useCallback(token => setTokenModalSource(token), [
|
||||
setTokenModalSource,
|
||||
]);
|
||||
|
||||
return (
|
||||
<Switch>
|
||||
<Route key="add" path="/users/:id/tokens/add">
|
||||
<UserTokenAdd id={Number(id)} />
|
||||
</Route>
|
||||
<Route key="token" path="/users/:id/tokens/:tokenId">
|
||||
<UserToken user={user} setBreadcrumb={setBreadcrumb} id={Number(id)} />
|
||||
</Route>
|
||||
<Route key="list" path="/users/:id/tokens">
|
||||
<UserTokenList id={Number(id)} />
|
||||
</Route>
|
||||
</Switch>
|
||||
<>
|
||||
<Switch>
|
||||
<Route key="add" path="/users/:id/tokens/add">
|
||||
<UserTokenAdd id={Number(id)} onSuccessfulAdd={onSuccessfulAdd} />
|
||||
</Route>
|
||||
<Route key="token" path="/users/:id/tokens/:tokenId">
|
||||
<UserToken
|
||||
user={user}
|
||||
setBreadcrumb={setBreadcrumb}
|
||||
id={Number(id)}
|
||||
/>
|
||||
</Route>
|
||||
<Route key="list" path="/users/:id/tokens">
|
||||
<UserTokenList id={Number(id)} />
|
||||
</Route>
|
||||
</Switch>
|
||||
{tokenModalSource && (
|
||||
<Modal
|
||||
aria-label={i18n._(t`Token information`)}
|
||||
isOpen
|
||||
variant="medium"
|
||||
title={i18n._(t`Token information`)}
|
||||
onClose={() => setTokenModalSource(null)}
|
||||
>
|
||||
<DetailList stacked>
|
||||
{tokenModalSource.token && (
|
||||
<Detail
|
||||
label={i18n._(t`Token`)}
|
||||
value={
|
||||
<ClipboardCopy
|
||||
isReadOnly
|
||||
variant={ClipboardCopyVariant.expansion}
|
||||
>
|
||||
{tokenModalSource.token}
|
||||
</ClipboardCopy>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{tokenModalSource.refresh_token && (
|
||||
<Detail
|
||||
label={i18n._(t`Refresh Token`)}
|
||||
value={
|
||||
<ClipboardCopy
|
||||
isReadOnly
|
||||
variant={ClipboardCopyVariant.expansion}
|
||||
>
|
||||
{tokenModalSource.refresh_token}
|
||||
</ClipboardCopy>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<Detail
|
||||
label={i18n._(t`Expires`)}
|
||||
value={formatDateString(tokenModalSource.expires)}
|
||||
/>
|
||||
</DetailList>
|
||||
</Modal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
41
awx/ui_next/src/screens/User/UserTokens/UserTokens.test.jsx
Normal file
41
awx/ui_next/src/screens/User/UserTokens/UserTokens.test.jsx
Normal file
@ -0,0 +1,41 @@
|
||||
import React from 'react';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
|
||||
|
||||
import UserTokens from './UserTokens';
|
||||
|
||||
describe('<UserTokens />', () => {
|
||||
let wrapper;
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test('renders successfully', () => {
|
||||
wrapper = mountWithContexts(<UserTokens />);
|
||||
expect(wrapper.length).toBe(1);
|
||||
});
|
||||
|
||||
test('shows Application information modal after successful creation', async () => {
|
||||
const history = createMemoryHistory({
|
||||
initialEntries: ['/users/1/tokens/add'],
|
||||
});
|
||||
wrapper = mountWithContexts(<UserTokens />, {
|
||||
context: { router: { history } },
|
||||
});
|
||||
expect(wrapper.find('Modal[title="Token information"]').length).toBe(0);
|
||||
await act(async () => {
|
||||
wrapper
|
||||
.find('UserTokenAdd')
|
||||
.props()
|
||||
.onSuccessfulAdd({
|
||||
expires: '3020-03-28T14:26:48.099297Z',
|
||||
token: 'foobar',
|
||||
refresh_token: 'aaaaaaaaaaaaaaaaaaaaaaaaaa',
|
||||
});
|
||||
});
|
||||
wrapper.update();
|
||||
expect(wrapper.find('Modal[title="Token information"]').length).toBe(1);
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user