0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-03-11 02:12:21 -05:00

Added test for replies and deleted members

refs https://github.com/TryGhost/Team/issues/1780
This commit is contained in:
Simon Backx 2022-08-12 11:19:21 +02:00
parent 2d79ef7cf9
commit 42d69562d9
8 changed files with 254 additions and 104 deletions

View file

@ -18,7 +18,7 @@
"@sentry/react": "^7.5.0",
"@testing-library/jest-dom": "5.16.2",
"@testing-library/react": "12.1.2",
"@testing-library/user-event": "14.0.0",
"@testing-library/user-event": "^14.4.3",
"@tiptap/core": "^2.0.0-beta.182",
"@tiptap/extension-blockquote": "^2.0.0-beta.29",
"@tiptap/extension-document": "^2.0.0-beta.17",
@ -87,7 +87,6 @@
"postcss": "^8.4.14",
"rewire": "6.0.0",
"serve-handler": "6.1.3",
"sinon": "^14.0.0",
"source-map-explorer": "2.5.2",
"tailwindcss": "^3.1.4",
"webpack-cli": "3.3.12"

View file

@ -1,9 +1,8 @@
import {render, within} from '@testing-library/react';
import {render, within, waitFor, act, fireEvent} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import App from './App';
import {ROOT_DIV_ID} from './utils/constants';
import {buildComment} from './utils/test-utils';
const sinon = require('sinon');
import {buildComment, buildMember} from './utils/test-utils';
function renderApp({member = null, documentStyles = {}, props = {}} = {}) {
const postId = 'my-post';
@ -24,6 +23,37 @@ function renderApp({member = null, documentStyles = {}, props = {}} = {}) {
comments: [],
meta: {
pagination: {
limit: 5,
total: 0,
next: null,
prev: null,
page: 1
}
}
};
},
add: async ({comment}) => {
return {
comments: [
{
...buildComment(),
...comment,
member,
replies: [],
liked: false,
count: {
likes: 0
}
}
]
};
},
replies: async () => {
return {
comments: [],
meta: {
pagination: {
limit: 3,
total: 0,
next: null,
prev: null,
@ -44,6 +74,14 @@ function renderApp({member = null, documentStyles = {}, props = {}} = {}) {
return {container, api, iframeDocument};
}
beforeEach(() => {
window.scrollTo = jest.fn();
});
afterEach(() => {
jest.restoreAllMocks();
});
describe('Auth frame', () => {
it('renders the auth frame', () => {
const {container} = renderApp();
@ -91,13 +129,14 @@ describe('Dark mode', () => {
describe('Comments', () => {
it('renders comments', async () => {
const {api, iframeDocument} = renderApp();
sinon.stub(api.comments, 'browse').callsFake(() => {
jest.spyOn(api.comments, 'browse').mockImplementation(() => {
return {
comments: [
buildComment({html: '<p>This is a comment body</p>'})
],
meta: {
pagination: {
limit: 5,
total: 1,
next: null,
prev: null,
@ -110,4 +149,187 @@ describe('Comments', () => {
const commentBody = await within(iframeDocument).findByText(/This is a comment body/i);
expect(commentBody).toBeInTheDocument();
});
it('shows pagination button on top', async () => {
const user = userEvent.setup();
const limit = 5;
const {api, iframeDocument} = renderApp();
jest.spyOn(api.comments, 'browse').mockImplementation(({page}) => {
if (page === 2) {
return {
comments: new Array(1).fill({}).map(() => buildComment({html: '<p>This is a paginated comment</p>'})),
meta: {
pagination: {
limit,
total: limit + 1,
next: null,
prev: 1,
page
}
}
};
}
return {
comments: new Array(limit).fill({}).map(() => buildComment({html: '<p>This is a comment body</p>'})),
meta: {
pagination: {
limit,
total: limit + 1,
next: 2,
prev: null,
page
}
}
};
});
const comments = await within(iframeDocument).findAllByText(/This is a comment body/i);
expect(comments).toHaveLength(limit);
const button = await within(iframeDocument).findByText(/Show 1 previous comment/i);
await user.click(button);
await within(iframeDocument).findByText(/This is a paginated comment/i);
expect(button).not.toBeInTheDocument();
});
it('can handle deleted members', async () => {
const limit = 5;
const {api, iframeDocument} = renderApp();
jest.spyOn(api.comments, 'browse').mockImplementation(({page}) => {
return {
comments: new Array(limit).fill({}).map(() => buildComment({html: '<p>This is a comment body</p>', member: null})),
meta: {
pagination: {
limit,
total: limit + 1,
next: 2,
prev: null,
page
}
}
};
});
const comments = await within(iframeDocument).findAllByText(/This is a comment body/i);
expect(comments).toHaveLength(limit);
});
it('shows a different UI when not logged in', async () => {
const limit = 5;
const {api, iframeDocument} = renderApp();
jest.spyOn(api.comments, 'browse').mockImplementation(({page}) => {
if (page === 2) {
throw new Error('Not requested');
}
return {
comments: new Array(limit).fill({}).map(() => buildComment({html: '<p>This is a comment body</p>'})),
meta: {
pagination: {
limit,
total: limit + 1,
next: 2,
prev: null,
page
}
}
};
});
const comments = await within(iframeDocument).findAllByText(/This is a comment body/i);
expect(comments).toHaveLength(limit);
// Does not show the reply buttons if not logged in
const replyButton = within(iframeDocument).queryByTestId('reply-button');
expect(replyButton).toBeNull(); // it doesn't exist
// Does not show the main form
const form = within(iframeDocument).queryByTestId('form');
expect(form).toBeNull(); // it doesn't exist
// todo: Does show the CTA
});
it('can reply to a comment', async () => {
const limit = 5;
const member = buildMember();
const {api, iframeDocument} = renderApp({
member
});
jest.spyOn(api.comments, 'browse').mockImplementation(({page}) => {
if (page === 2) {
throw new Error('Not requested');
}
return {
comments: new Array(limit).fill({}).map(() => buildComment({html: '<p>This is a comment body</p>'})),
meta: {
pagination: {
limit,
total: limit + 1,
next: 2,
prev: null,
page
}
}
};
});
const repliesSpy = jest.spyOn(api.comments, 'replies');
const comments = await within(iframeDocument).findAllByTestId('comment-component');
expect(comments).toHaveLength(limit);
// Does show the main form
const form = within(iframeDocument).queryByTestId('form');
expect(form).toBeInTheDocument();
const replyButton = within(comments[0]).queryByTestId('reply-button');
expect(replyButton).toBeInTheDocument();
await userEvent.click(replyButton);
const replyForm = within(comments[0]).queryByTestId('form');
expect(replyForm).toBeInTheDocument();
// todo: Check if the main form has been hidden
expect(repliesSpy).toBeCalledTimes(1);
// Enter some text
const editor = replyForm.querySelector('[contenteditable="true"]');
await act(async () => {
await userEvent.type(editor, '> This is a quote');
fireEvent.keyDown(editor, {key: 'Enter', code: 'Enter', charCode: 13});
fireEvent.keyDown(editor, {key: 'Enter', code: 'Enter', charCode: 13});
await userEvent.type(editor, 'This is a reply');
});
// Press save
const submitButton = within(replyForm).queryByTestId('submit-form-button');
expect(submitButton).toBeInTheDocument();
await userEvent.click(submitButton);
// Form should get removed
await waitFor(() => {
expect(replyForm).not.toBeInTheDocument();
});
// Check if reply is visible
const replies = within(comments[0]).queryAllByTestId('comment-component');
expect(replies).toHaveLength(1);
const content = within(replies[0]).queryByTestId('comment-content');
expect(content.innerHTML).toEqual('<blockquote><p>This is a quote</p></blockquote><p></p><p>This is a reply</p>');
// Check if pagination button is NOT visible
const replyPagination = within(iframeDocument).queryByTestId('reply-pagination-button');
expect(replyPagination).toBeNull(); // it doesn't exist
});
});

View file

@ -89,7 +89,7 @@ const Comment = ({updateIsEditing = null, isEditing, ...props}) => {
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className={`flex flex-row w-full ${hasReplies ? 'mb-0' : 'mb-10'}`}>
<div className={`flex flex-row w-full ${hasReplies ? 'mb-0' : 'mb-10'}`} data-testid="comment-component">
<div className="mr-3 flex flex-col justify-start items-center">
<div className="flex-0 mb-4">
<Avatar comment={comment} saturation={avatarSaturation} isBlank={isNotPublished} />
@ -119,7 +119,7 @@ const Comment = ({updateIsEditing = null, isEditing, ...props}) => {
{!isNotPublished &&
<div className="flex flex-row items-center gap-4 mt mb-2 pr-4">
<p dangerouslySetInnerHTML={html} className="gh-comment-content font-sans leading-normal text-[16px] text-neutral-900 dark:text-[rgba(255,255,255,0.85)]" />
<p dangerouslySetInnerHTML={html} className="gh-comment-content font-sans leading-normal text-[16px] text-neutral-900 dark:text-[rgba(255,255,255,0.85)]" data-testid="comment-content"/>
</div>}
{!isNotPublished && (

View file

@ -408,7 +408,7 @@ const Form = (props) => {
return (
<>
<form ref={formEl} onClick={focusEditor} onMouseDown={preventIfFocused} onTouchStart={preventIfFocused} className={`
<form ref={formEl} data-testid="form" onClick={focusEditor} onMouseDown={preventIfFocused} onTouchStart={preventIfFocused} className={`
transition duration-200
pt-3 pb-2 px-3
-mt-[14px] -mx-3 mb-10
@ -458,6 +458,7 @@ const Form = (props) => {
text-white dark:text-neutral-800
`}
type="button"
data-testid="submit-form-button"
onClick={submitForm}
>
<span>{buttonIcon}</span>

View file

@ -5,7 +5,7 @@ const RepliesPagination = (props) => {
const count = props.count;
return (
<button className="group w-full text-neutral-700 font-semibold mt-10 sm:mt-0 mb-10 font-sans text-md text-left dark:text-white flex items-center " onClick={loadMore}>
<button className="group w-full text-neutral-700 font-semibold mt-10 sm:mt-0 mb-10 font-sans text-md text-left dark:text-white flex items-center " onClick={loadMore} data-testid="reply-pagination-button">
<span className="whitespace-nowrap mr-4"> Show {count} more {count === 1 ? 'reply' : 'replies'}</span>
</button>
);

View file

@ -6,7 +6,7 @@ function Reply(props) {
const {member} = useContext(AppContext);
return member ?
(<button disabled={!!props.disabled} className={`group transition-all duration-50 ease-linear flex font-sans items-center text-sm outline-0 ${props.isReplying ? 'text-neutral-900 dark:text-[rgba(255,255,255,0.9)]' : 'text-neutral-400 dark:text-[rgba(255,255,255,0.5)] hover:text-neutral-600'}`} onClick={props.toggleReply}>
(<button disabled={!!props.disabled} className={`group transition-all duration-50 ease-linear flex font-sans items-center text-sm outline-0 ${props.isReplying ? 'text-neutral-900 dark:text-[rgba(255,255,255,0.9)]' : 'text-neutral-400 dark:text-[rgba(255,255,255,0.5)] hover:text-neutral-600'}`} onClick={props.toggleReply} data-testid="reply-button">
<ReplyIcon className={`mr-[6px] ${props.isReplying ? 'fill-neutral-900 stroke-neutral-900 dark:fill-white dark:stroke-white' : 'stroke-neutral-400 dark:stroke-[rgba(255,255,255,0.5)] group-hover:stroke-neutral-600'} transition duration-50 ease-linear`} />Reply
</button>) : null;
}

View file

@ -1,5 +1,16 @@
const ObjectId = require('bson-objectid').default;
export function buildMember(override) {
return {
avatar_image: '',
bio: 'Head of Testing',
id: ObjectId(),
name: 'Test Member',
uuid: '613e9667-4fa2-4ff4-aa62-507220103d41',
...override
};
}
export function buildComment(override) {
return {
id: ObjectId(),
@ -12,13 +23,7 @@ export function buildComment(override) {
liked: false,
created_at: '2022-08-11T09:26:34.000Z',
edited_at: null,
member: {
avatar_image: '',
bio: 'Head of Testing',
id: ObjectId(),
name: 'Test Member',
uuid: '613e9667-4fa2-4ff4-aa62-507220103d41'
},
member: buildMember(),
status: 'published',
...override
};
@ -34,13 +39,7 @@ export function buildReply(override) {
liked: false,
created_at: '2022-08-11T09:26:34.000Z',
edited_at: null,
member: {
avatar_image: '',
bio: 'Head of Testing',
id: ObjectId(),
name: 'Test Member',
uuid: '613e9667-4fa2-4ff4-aa62-507220103d41'
},
member: buildMember(),
status: 'published',
...override
};

View file

@ -1571,20 +1571,13 @@
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.23.5.tgz#93f7b9f4e3285a7a9ade7557d9a8d36809cbc47d"
integrity sha512-AFBVi/iT4g20DHoujvMH1aEDn8fGJh4xsRGCP6d8RpLPMqsNPvW01Jcn0QysXTsg++/xj25NmJsGyH9xug/wKg==
"@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.3":
"@sinonjs/commons@^1.7.0":
version "1.8.3"
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d"
integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==
dependencies:
type-detect "4.0.8"
"@sinonjs/fake-timers@>=5", "@sinonjs/fake-timers@^9.1.2":
version "9.1.2"
resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c"
integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==
dependencies:
"@sinonjs/commons" "^1.7.0"
"@sinonjs/fake-timers@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40"
@ -1592,20 +1585,6 @@
dependencies:
"@sinonjs/commons" "^1.7.0"
"@sinonjs/samsam@^6.1.1":
version "6.1.1"
resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-6.1.1.tgz#627f7f4cbdb56e6419fa2c1a3e4751ce4f6a00b1"
integrity sha512-cZ7rKJTLiE7u7Wi/v9Hc2fs3Ucc3jrWeMgPHbbTCeVAB2S0wOBbYlkJVeNSL04i7fdhT8wIbDq1zhC/PXTD2SA==
dependencies:
"@sinonjs/commons" "^1.6.0"
lodash.get "^4.4.2"
type-detect "^4.0.8"
"@sinonjs/text-encoding@^0.7.1":
version "0.7.2"
resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz#5981a8db18b56ba38ef0efb7d995b12aa7b51918"
integrity sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==
"@surma/rollup-plugin-off-main-thread@^1.1.1":
version "1.4.2"
resolved "https://registry.yarnpkg.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-1.4.2.tgz#e6786b6af5799f82f7ab3a82e53f6182d2b91a58"
@ -1754,10 +1733,10 @@
"@babel/runtime" "^7.12.5"
"@testing-library/dom" "^8.0.0"
"@testing-library/user-event@14.0.0":
version "14.0.0"
resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.0.0.tgz#3906aa6f0e56fd012d73559f5f05c02e63ba18dd"
integrity sha512-hZhjNrle/DMi1ziHwHy8LS0fYJtf+cID7fuG5+4h+Bk83xQaRDQT/DlqfL4hJYw3mxW6KTIxoODrhGnhqJebdQ==
"@testing-library/user-event@^14.4.3":
version "14.4.3"
resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.4.3.tgz#af975e367743fa91989cd666666aec31a8f50591"
integrity sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q==
"@tiptap/core@^2.0.0-beta.182":
version "2.0.0-beta.182"
@ -4450,11 +4429,6 @@ diff-sequences@^28.1.1:
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6"
integrity sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==
diff@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40"
integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==
diffie-hellman@^5.0.0:
version "5.0.3"
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
@ -6802,11 +6776,6 @@ is-wsl@^2.1.1, is-wsl@^2.2.0:
dependencies:
is-docker "^2.0.0"
isarray@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==
isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
@ -7482,11 +7451,6 @@ jsonfile@^6.0.1:
array-includes "^3.1.5"
object.assign "^4.1.2"
just-extend@^4.0.2:
version "4.2.1"
resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744"
integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==
killable@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"
@ -7661,11 +7625,6 @@ lodash.debounce@^4.0.8:
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
lodash.get@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==
lodash.kebabcase@4.1.1, lodash.kebabcase@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36"
@ -8188,17 +8147,6 @@ nice-try@^1.0.4:
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
nise@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.1.tgz#ac4237e0d785ecfcb83e20f389185975da5c31f3"
integrity sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A==
dependencies:
"@sinonjs/commons" "^1.8.3"
"@sinonjs/fake-timers" ">=5"
"@sinonjs/text-encoding" "^0.7.1"
just-extend "^4.0.2"
path-to-regexp "^1.7.0"
no-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
@ -8779,13 +8727,6 @@ path-to-regexp@2.2.1:
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.2.1.tgz#90b617025a16381a879bc82a38d4e8bdeb2bcf45"
integrity sha512-gu9bD6Ta5bwGrrU8muHzVOBFFREpp2iRkVfhBJahwJ6p6Xw20SjT0MxLnwkjOibQmGSYhiUnf2FLe7k+jcFmGQ==
path-to-regexp@^1.7.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a"
integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==
dependencies:
isarray "0.0.1"
path-type@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
@ -10880,18 +10821,6 @@ simple-swizzle@^0.2.2:
dependencies:
is-arrayish "^0.3.1"
sinon@^14.0.0:
version "14.0.0"
resolved "https://registry.yarnpkg.com/sinon/-/sinon-14.0.0.tgz#203731c116d3a2d58dc4e3cbe1f443ba9382a031"
integrity sha512-ugA6BFmE+WrJdh0owRZHToLd32Uw3Lxq6E6LtNRU+xTVBefx632h03Q7apXWRsRdZAJ41LB8aUfn2+O4jsDNMw==
dependencies:
"@sinonjs/commons" "^1.8.3"
"@sinonjs/fake-timers" "^9.1.2"
"@sinonjs/samsam" "^6.1.1"
diff "^5.0.0"
nise "^5.1.1"
supports-color "^7.2.0"
sisteransi@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
@ -11422,7 +11351,7 @@ supports-color@^6.1.0:
dependencies:
has-flag "^3.0.0"
supports-color@^7.0.0, supports-color@^7.1.0, supports-color@^7.2.0:
supports-color@^7.0.0, supports-color@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
@ -11803,7 +11732,7 @@ type-check@~0.3.2:
dependencies:
prelude-ls "~1.1.2"
type-detect@4.0.8, type-detect@^4.0.8:
type-detect@4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==