From 5dc4396f6b3bc283e74a49e1734e0102ca63e513 Mon Sep 17 00:00:00 2001 From: Gary Borton Date: Fri, 13 Jan 2023 19:36:04 -0800 Subject: [PATCH] Switch from cypress to playwright. (#5248) * empty * empty * empty * Begin move from cypress to playwright. * Switch remaining tests to playwright, remove old cypress suppport files. * Clean up playwright config * Enable ff, and safari when on mac. * Fix safari/ff mentions test * Fix code-highlighting test on ff/safari * Add a local retry as a few tests are flaky. * Replace cypress w/ playwright in gitignore. * Update to latest yarn to fix ci install? * Update yarn.lock w/ yarn command. * Fix mocha tests. * Fix prettier --- .gitignore | 5 +- cypress.json | 4 - cypress/integration/examples/check-lists.ts | 35 -- .../integration/examples/code-highlighting.ts | 42 -- .../integration/examples/editable-voids.ts | 34 -- cypress/integration/examples/embeds.ts | 14 - cypress/integration/examples/forced-layout.ts | 27 - .../integration/examples/hovering-toolbar.ts | 38 -- cypress/integration/examples/huge-document.ts | 16 - cypress/integration/examples/iframe.ts | 36 -- cypress/integration/examples/images.ts | 12 - cypress/integration/examples/inlines.ts | 11 - .../integration/examples/markdown-preview.ts | 19 - .../examples/markdown-shortcuts.ts | 52 -- cypress/integration/examples/mentions.ts | 28 - cypress/integration/examples/paste-html.ts | 29 - cypress/integration/examples/placeholder.ts | 12 - cypress/integration/examples/plaintext.ts | 10 - cypress/integration/examples/read-only.ts | 15 - cypress/integration/examples/richtext.ts | 18 - .../examples/search-highlighting.ts | 11 - cypress/integration/examples/select.ts | 62 --- cypress/integration/examples/shadow-dom.ts | 10 - cypress/integration/examples/tables.ts | 11 - cypress/support/commands.ts | 15 - cypress/support/index.ts | 1 - package.json | 8 +- packages/slate-react/test/index.spec.tsx | 4 +- playwright.config.ts | 90 +++ .../integration/examples/check-lists.test.ts | 50 ++ .../examples/code-highlighting.test.ts | 57 ++ .../examples/editable-voids.test.ts | 39 ++ .../integration/examples/embeds.test.ts | 18 + .../examples/forced-layout.test.ts | 28 + .../examples/hovering-toolbar.test.ts | 49 ++ .../examples/huge-document.test.ts | 18 + .../integration/examples/iframe.test.ts | 24 + .../integration/examples/images.test.ts | 16 + .../integration/examples/inlines.test.ts | 17 + .../examples/markdown-preview.test.ts | 32 ++ .../examples/markdown-shortcuts.test.ts | 78 +++ .../integration/examples/mentions.test.ts | 30 + .../integration/examples/paste-html.test.ts | 38 ++ .../integration/examples/placeholder.test.ts | 17 + .../integration/examples/plaintext.test.ts | 16 + .../integration/examples/read-only.test.ts | 17 + .../integration/examples/richtext.test.ts | 29 + .../examples/search-highlighting.test.ts | 16 + .../integration/examples/select.test.ts | 28 + .../integration/examples/shadow-dom.test.ts | 15 + .../integration/examples/tables.test.ts | 16 + {cypress => playwright}/tsconfig.json | 1 - yarn.lock | 527 ++---------------- 53 files changed, 787 insertions(+), 1058 deletions(-) delete mode 100644 cypress.json delete mode 100644 cypress/integration/examples/check-lists.ts delete mode 100644 cypress/integration/examples/code-highlighting.ts delete mode 100644 cypress/integration/examples/editable-voids.ts delete mode 100644 cypress/integration/examples/embeds.ts delete mode 100644 cypress/integration/examples/forced-layout.ts delete mode 100644 cypress/integration/examples/hovering-toolbar.ts delete mode 100644 cypress/integration/examples/huge-document.ts delete mode 100644 cypress/integration/examples/iframe.ts delete mode 100644 cypress/integration/examples/images.ts delete mode 100644 cypress/integration/examples/inlines.ts delete mode 100644 cypress/integration/examples/markdown-preview.ts delete mode 100644 cypress/integration/examples/markdown-shortcuts.ts delete mode 100644 cypress/integration/examples/mentions.ts delete mode 100644 cypress/integration/examples/paste-html.ts delete mode 100644 cypress/integration/examples/placeholder.ts delete mode 100644 cypress/integration/examples/plaintext.ts delete mode 100644 cypress/integration/examples/read-only.ts delete mode 100644 cypress/integration/examples/richtext.ts delete mode 100644 cypress/integration/examples/search-highlighting.ts delete mode 100644 cypress/integration/examples/select.ts delete mode 100644 cypress/integration/examples/shadow-dom.ts delete mode 100644 cypress/integration/examples/tables.ts delete mode 100644 cypress/support/commands.ts delete mode 100644 cypress/support/index.ts create mode 100644 playwright.config.ts create mode 100644 playwright/integration/examples/check-lists.test.ts create mode 100644 playwright/integration/examples/code-highlighting.test.ts create mode 100644 playwright/integration/examples/editable-voids.test.ts create mode 100644 playwright/integration/examples/embeds.test.ts create mode 100644 playwright/integration/examples/forced-layout.test.ts create mode 100644 playwright/integration/examples/hovering-toolbar.test.ts create mode 100644 playwright/integration/examples/huge-document.test.ts create mode 100644 playwright/integration/examples/iframe.test.ts create mode 100644 playwright/integration/examples/images.test.ts create mode 100644 playwright/integration/examples/inlines.test.ts create mode 100644 playwright/integration/examples/markdown-preview.test.ts create mode 100644 playwright/integration/examples/markdown-shortcuts.test.ts create mode 100644 playwright/integration/examples/mentions.test.ts create mode 100644 playwright/integration/examples/paste-html.test.ts create mode 100644 playwright/integration/examples/placeholder.test.ts create mode 100644 playwright/integration/examples/plaintext.test.ts create mode 100644 playwright/integration/examples/read-only.test.ts create mode 100644 playwright/integration/examples/richtext.test.ts create mode 100644 playwright/integration/examples/search-highlighting.test.ts create mode 100644 playwright/integration/examples/select.test.ts create mode 100644 playwright/integration/examples/shadow-dom.test.ts create mode 100644 playwright/integration/examples/tables.test.ts rename {cypress => playwright}/tsconfig.json (96%) diff --git a/.gitignore b/.gitignore index e35eb9544e..c408a43352 100644 --- a/.gitignore +++ b/.gitignore @@ -9,10 +9,7 @@ node_modules/ packages/*/yarn.lock site/out/ tmp/ -cypress/screenshots -cypress/videos -cypress/fixtures -cypress/plugins +test-results/ .DS_Store # Recommendation from https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored (not using Zero-installs) diff --git a/cypress.json b/cypress.json deleted file mode 100644 index 570df31c06..0000000000 --- a/cypress.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "projectId": "slate-react", - "baseUrl": "http://localhost:3000" -} diff --git a/cypress/integration/examples/check-lists.ts b/cypress/integration/examples/check-lists.ts deleted file mode 100644 index 02447fa194..0000000000 --- a/cypress/integration/examples/check-lists.ts +++ /dev/null @@ -1,35 +0,0 @@ -describe('Check-lists example', () => { - beforeEach(() => { - cy.visit('examples/check-lists') - }) - - it('checks the bullet when clicked', () => { - const slateNodeElement = 'div[data-slate-node="element"]' - - cy.get(slateNodeElement).should('have.length', 6) - - cy.get(slateNodeElement) - .eq(3) - .should('contain', 'Criss-cross!') - .find('span') - .eq(1) - .should('have.css', 'text-decoration-line', 'line-through') - - // Unchecking the checkboxes should un-cross the corresponding text. - cy.get(slateNodeElement) - .eq(3) - .should('contain', 'Criss-cross!') - .find('span') - .eq(0) - .find('input') - .uncheck() - .get(slateNodeElement) - .eq(3) - .should('contain', 'Criss-cross!') - .find('span') - .eq(1) - .should('have.css', 'text-decoration-line', 'none') - - cy.get('p[data-slate-node="element"]').should('have.length', 2) - }) -}) diff --git a/cypress/integration/examples/code-highlighting.ts b/cypress/integration/examples/code-highlighting.ts deleted file mode 100644 index d4d4353a2f..0000000000 --- a/cypress/integration/examples/code-highlighting.ts +++ /dev/null @@ -1,42 +0,0 @@ -describe('code highlighting', () => { - const slateEditor = '[data-slate-node="element"]' - const leafNode = 'span[data-slate-leaf="true"]' - - beforeEach(() => { - cy.visit('examples/code-highlighting') - }) - - it('highlights HTML tags', () => { - cy.get(slateEditor) - .find('span') - .eq(0) - .find(leafNode) - .eq(0) - .should('contain', '

') - .should('have.css', 'color', 'rgb(153, 0, 85)') - }) - - it( - 'highlights javascript syntax', - { - defaultCommandTimeout: 10000, // test was not passing within 4s default - }, - () => { - const JSCode = 'const slateVar = 30;{enter}' - cy.get('select').select('JavaScript') // Select the 'JavaScript' option - cy.get('select').should('have.value', 'js') // Confirm value to avoid race condition - - cy.get(slateEditor) - .type('{movetostart}') - .type(JSCode) // Type JavaScript code - - cy.get(slateEditor) - .find('span') - .eq(0) - .find(leafNode) - .eq(0) - .should('contain', 'const') - .should('have.css', 'color', 'rgb(0, 119, 170)') - } - ) -}) diff --git a/cypress/integration/examples/editable-voids.ts b/cypress/integration/examples/editable-voids.ts deleted file mode 100644 index d33de6fbf5..0000000000 --- a/cypress/integration/examples/editable-voids.ts +++ /dev/null @@ -1,34 +0,0 @@ -describe('editable voids', () => { - const input = 'input[type="text"]' - const elements = [ - { tag: 'h4', count: 3 }, - { tag: input, count: 1 }, - { tag: 'input[type="radio"]', count: 2 }, - ] - - beforeEach(() => { - cy.visit('examples/editable-voids') - }) - - it('checks for the elements', () => { - elements.forEach(({ tag, count }) => { - cy.get(tag).should('have.length', count) - }) - }) - - it('should double the elements', () => { - // click the `+` sign to duplicate the editable void - cy.get('span.material-icons') - .eq(1) - .click() - - elements.forEach(({ tag, count }) => { - cy.get(tag).should('have.length', count * 2) - }) - }) - - it('make sure you can edit editable void', () => { - cy.get(input).type('Typing') - cy.get(input).should('have.value', 'Typing') - }) -}) diff --git a/cypress/integration/examples/embeds.ts b/cypress/integration/examples/embeds.ts deleted file mode 100644 index f019b7f3f1..0000000000 --- a/cypress/integration/examples/embeds.ts +++ /dev/null @@ -1,14 +0,0 @@ -describe('embeds example', () => { - const slateEditor = 'div[data-slate-editor="true"]' - - beforeEach(() => { - cy.visit('examples/embeds') - }) - - it('contains embeded', () => { - cy.get(slateEditor) - .find('iframe') - .should('exist') - .should('have.length', 1) - }) -}) diff --git a/cypress/integration/examples/forced-layout.ts b/cypress/integration/examples/forced-layout.ts deleted file mode 100644 index c7d6ed096b..0000000000 --- a/cypress/integration/examples/forced-layout.ts +++ /dev/null @@ -1,27 +0,0 @@ -describe('forced layout example', () => { - const elements = [ - { tag: '#__next h2', count: 1 }, - { tag: '#__next p', count: 1 }, - ] - - beforeEach(() => { - cy.visit('examples/forced-layout') - }) - - it('checks for the elements', () => { - elements.forEach(({ tag, count }) => { - cy.get(tag).should('have.length', count) - }) - }) - - it('checks if elements persist even after everything is deleted', () => { - // clear the textbox - cy.get('div[role="textbox"]') - .type(`{selectall}`) - .clear() - - elements.forEach(({ tag, count }) => { - cy.get(tag).should('have.length', count) - }) - }) -}) diff --git a/cypress/integration/examples/hovering-toolbar.ts b/cypress/integration/examples/hovering-toolbar.ts deleted file mode 100644 index db8ea84099..0000000000 --- a/cypress/integration/examples/hovering-toolbar.ts +++ /dev/null @@ -1,38 +0,0 @@ -describe('hovering toolbar example', () => { - beforeEach(() => { - cy.visit('examples/hovering-toolbar') - }) - - it('hovering toolbar appears', () => { - cy.get('div') - .eq(12) - .should('not.exist') - - cy.get('span[data-slate-string="true"]') - .eq(0) - .type(`{selectall}`) - .get('div') - .eq(12) - .should('exist') - .should('have.css', 'opacity', '1') - .find('span.material-icons') - .should('have.length', 3) - }) - it('hovering toolbar disappears', () => { - cy.get('span[data-slate-string="true"]') - .eq(0) - .type(`{selectall}`) - .get('div') - .eq(12) - .should('exist') - .get('span[data-slate-string="true"]') - .eq(0) - .type(`{selectall}`) - .get('div') - .eq(0) - .click({ force: true }) - .get('div') - .eq(12) - .should('have.css', 'opacity', '0') - }) -}) diff --git a/cypress/integration/examples/huge-document.ts b/cypress/integration/examples/huge-document.ts deleted file mode 100644 index ca1f7a2ecb..0000000000 --- a/cypress/integration/examples/huge-document.ts +++ /dev/null @@ -1,16 +0,0 @@ -describe('huge document example', () => { - const elements = [ - { tag: '#__next h1', count: 100 }, - { tag: '#__next p', count: 700 }, - ] - - beforeEach(() => { - cy.visit('examples/huge-document') - }) - - it('contains image', () => { - elements.forEach(({ tag, count }) => { - cy.get(tag).should('have.length', count) - }) - }) -}) diff --git a/cypress/integration/examples/iframe.ts b/cypress/integration/examples/iframe.ts deleted file mode 100644 index 50ddf38dc1..0000000000 --- a/cypress/integration/examples/iframe.ts +++ /dev/null @@ -1,36 +0,0 @@ -// Taken from https://www.cypress.io/blog/2020/02/12/working-with-iframes-in-cypress/ -const getIframeDocument = () => { - return ( - cy - // adding the wait here because the IFrame component re-renders a bunch of times at startup which can mess up this test. - .wait(1000) - .get('iframe') - .its('0.contentDocument') - .should('exist') - ) -} - -const getIframeBody = () => { - return ( - getIframeDocument() - .its('body') - // automatically retries until body is loaded - .should('not.be.undefined') - .should('not.be.null') - .then(cy.wrap) - ) -} - -describe('iframe editor', () => { - beforeEach(() => { - cy.visit('examples/iframe') - }) - - it('should be editable', () => { - getIframeBody() - .findByRole('textbox') - .type('{movetostart}') - .type('Hello World') - .should('contain.text', 'Hello World') - }) -}) diff --git a/cypress/integration/examples/images.ts b/cypress/integration/examples/images.ts deleted file mode 100644 index 57f5256a3f..0000000000 --- a/cypress/integration/examples/images.ts +++ /dev/null @@ -1,12 +0,0 @@ -describe('images example', () => { - beforeEach(() => { - cy.visit('examples/images') - }) - - it('contains image', () => { - cy.findByRole('textbox') - .find('img') - .should('exist') - .should('have.length', 2) - }) -}) diff --git a/cypress/integration/examples/inlines.ts b/cypress/integration/examples/inlines.ts deleted file mode 100644 index cccb860473..0000000000 --- a/cypress/integration/examples/inlines.ts +++ /dev/null @@ -1,11 +0,0 @@ -describe('Inlines example', () => { - beforeEach(() => { - cy.visit('examples/inlines') - }) - - it('contains link', () => { - cy.findByRole('textbox') - .find('a') - .should('contain.text', 'hyperlink') - }) -}) diff --git a/cypress/integration/examples/markdown-preview.ts b/cypress/integration/examples/markdown-preview.ts deleted file mode 100644 index 18f4978f3f..0000000000 --- a/cypress/integration/examples/markdown-preview.ts +++ /dev/null @@ -1,19 +0,0 @@ -describe('markdown preview', () => { - const slateEditor = 'div[data-slate-editor="true"]' - const markdown = 'span[data-slate-string="true"]' - - beforeEach(() => { - cy.visit('examples/markdown-preview') - }) - - it('checks for markdown', () => { - cy.get(slateEditor) - .find(markdown) - .should('have.length', 9) - - cy.get(slateEditor) - .type('{movetoend}{enter}## Try it out!{enter}') - .find(markdown) - .should('have.length', 10) - }) -}) diff --git a/cypress/integration/examples/markdown-shortcuts.ts b/cypress/integration/examples/markdown-shortcuts.ts deleted file mode 100644 index c26fb93fa8..0000000000 --- a/cypress/integration/examples/markdown-shortcuts.ts +++ /dev/null @@ -1,52 +0,0 @@ -describe('On markdown-shortcuts example', () => { - beforeEach(() => { - cy.visit('examples/markdown-shortcuts') - }) - - it('contains quote', () => { - cy.findByRole('textbox') - .find('blockquote') - .should('contain.text', 'A wise quote.') - }) - - it('can add list items', () => { - cy.findByRole('textbox') - .find('ul') - .should('not.exist') - - cy.findByRole('textbox') - // need wait() here otherwise the slate component is not fully mounted yet sometimes - .wait(1000) - .type( - '{movetostart}* 1st Item{enter}2nd Item{enter}3rd Item{enter}{backspace}' - ) - - cy.get('ul > li') - - cy.get('ul > li').should('have.length', 3) - - cy.get('ul > li') - .eq(0) - .should('contain.text', '1st Item') - .get('ul > li') - .eq(1) - .should('contain.text', '2nd Item') - .get('ul > li') - .eq(2) - .should('contain.text', '3rd Item') - }) - - it('can add a h1 item', () => { - cy.findByRole('textbox') - .find('h1') - .should('not.exist') - - cy.findByRole('textbox').type('{enter}{leftarrow}# Heading') - - cy.get('h1').should('have.length', 1) - - cy.findByRole('textbox') - .find('h1') - .should('contain.text', 'Heading') - }) -}) diff --git a/cypress/integration/examples/mentions.ts b/cypress/integration/examples/mentions.ts deleted file mode 100644 index d393a0e024..0000000000 --- a/cypress/integration/examples/mentions.ts +++ /dev/null @@ -1,28 +0,0 @@ -describe('mentions example', () => { - beforeEach(() => cy.visit('examples/mentions')) - - it('renders mention element', () => { - cy.findByRole('textbox') - .dataCy('mention-R2-D2') - .should('exist') - .dataCy('mention-Mace-Windu') - .should('exist') - }) - - it('shows list of mentions', () => { - cy.findByRole('textbox') - .type('{movetoend}') - .type(' @ma') - .dataCy('mentions-portal') - .should('exist') - }) - - it('inserts on enter from list', () => { - cy.findByRole('textbox') - .type('{movetoend}') - .type(' @Ja') - .type('{enter}') - .dataCy('mention-Jabba') - .should('exist') - }) -}) diff --git a/cypress/integration/examples/paste-html.ts b/cypress/integration/examples/paste-html.ts deleted file mode 100644 index a47cb864d6..0000000000 --- a/cypress/integration/examples/paste-html.ts +++ /dev/null @@ -1,29 +0,0 @@ -describe('paste html example', () => { - beforeEach(() => cy.visit('examples/paste-html')) - - const createHtmlPasteEvent = (htmlContent: string) => - Object.assign(new Event('paste', { bubbles: true, cancelable: true }), { - clipboardData: { - getData: (type = 'text/html') => htmlContent, - types: ['text/html'], - }, - }) - - const cyNewPasteHtml = (htmlContent: string) => - cy - .findByRole('textbox') - .type('{selectall}') - .trigger('paste', createHtmlPasteEvent(htmlContent)) - - it('pasted bold text uses ', () => { - cyNewPasteHtml('Hello Bold') - .get('strong') - .should('contain.text', 'Hello') - }) - - it('pasted code uses ', () => { - cyNewPasteHtml('console.log("hello from slate!")') - .get('code') - .should('contain.text', 'slate!') - }) -}) diff --git a/cypress/integration/examples/placeholder.ts b/cypress/integration/examples/placeholder.ts deleted file mode 100644 index dee4f947e9..0000000000 --- a/cypress/integration/examples/placeholder.ts +++ /dev/null @@ -1,12 +0,0 @@ -describe('placeholder example', () => { - beforeEach(() => cy.visit('examples/custom-placeholder')) - - it('renders custom placeholder', () => { - const placeholderElement = cy.get('[data-slate-placeholder=true]') - - placeholderElement - .should('contain.text', 'Type something') - .get('pre') - .should('contain.text', 'renderPlaceholder') - }) -}) diff --git a/cypress/integration/examples/plaintext.ts b/cypress/integration/examples/plaintext.ts deleted file mode 100644 index 39a873c183..0000000000 --- a/cypress/integration/examples/plaintext.ts +++ /dev/null @@ -1,10 +0,0 @@ -describe('plaintext example', () => { - beforeEach(() => cy.visit('examples/plaintext')) - - it('inserts text when typed', () => { - cy.findByRole('textbox') - .type('{movetostart}') - .type('Hello World') - .should('contain.text', 'Hello World') - }) -}) diff --git a/cypress/integration/examples/read-only.ts b/cypress/integration/examples/read-only.ts deleted file mode 100644 index df77703622..0000000000 --- a/cypress/integration/examples/read-only.ts +++ /dev/null @@ -1,15 +0,0 @@ -describe('readonly editor', () => { - beforeEach(() => { - cy.visit('examples/read-only') - }) - - it('should not be editable', () => { - const slateEditor = '[data-slate-editor="true"]' - - cy.get(slateEditor) - .should('not.have.attr', 'contentEditable', 'true') - .should('not.have.attr', 'role', 'textbox') - .click() - .should('not.be.focused') - }) -}) diff --git a/cypress/integration/examples/richtext.ts b/cypress/integration/examples/richtext.ts deleted file mode 100644 index 1daf689871..0000000000 --- a/cypress/integration/examples/richtext.ts +++ /dev/null @@ -1,18 +0,0 @@ -describe('On richtext example', () => { - beforeEach(() => cy.visit('examples/richtext')) - - it('renders rich text', () => { - cy.findByRole('textbox') - .get('strong') - .should('contain.text', 'rich') - .get('blockquote') - .should('contain.text', 'wise quote') - }) - - it('inserts text when typed', () => { - cy.findByRole('textbox') - .type('{movetostart}') - .type('Hello World') - .should('contain.text', 'Hello World') - }) -}) diff --git a/cypress/integration/examples/search-highlighting.ts b/cypress/integration/examples/search-highlighting.ts deleted file mode 100644 index cf066b14af..0000000000 --- a/cypress/integration/examples/search-highlighting.ts +++ /dev/null @@ -1,11 +0,0 @@ -describe('search highlighting', () => { - beforeEach(() => cy.visit('examples/search-highlighting')) - - it('highlights the searched text', () => { - const searchField = 'input[type="search"]' - const highlightedText = 'search-highlighted' - - cy.get(searchField).type('text') - cy.dataCy(highlightedText).should('have.length', 2) - }) -}) diff --git a/cypress/integration/examples/select.ts b/cypress/integration/examples/select.ts deleted file mode 100644 index 648be040d3..0000000000 --- a/cypress/integration/examples/select.ts +++ /dev/null @@ -1,62 +0,0 @@ -describe('selection', () => { - // Currently, testing color property always yields rgb() value, even when stored - // as hex. - // Hence, we'll overwrite Cypress `should` command, in which we use - // `getComputedStyle` on both the subject element and creates a temp element - // to get the computed color and compares. - // Code by Nicholas Boll at https://github.com/cypress-io/cypress/issues/2186 - // 2021/08/27 - type CssStyleObject = Partial & - Record - const compareColor = (color: string, property: string) => ( - targetElement: NodeListOf - ) => { - const tempElement = document.createElement('div') - tempElement.style.color = color - tempElement.style.display = 'none' // make sure it doesn't actually render - document.body.appendChild(tempElement) // append so that `getComputedStyle` actually works - - const tempColor = getComputedStyle(tempElement).color - // Calling window.getComputedStyle(element) returns `CSSStyleDeclaration` - // object which has numeric index signature with string keys. - // We need to declare a new object which retains the typings of - // CSSStyleDeclaration and yet is relaxed enough to accept an - // arbitrary property name. - - const targetStyle = getComputedStyle(targetElement[0]) as CssStyleObject - const targetColor = targetStyle[property] - - document.body.removeChild(tempElement) // remove it because we're done with it - - expect(tempColor).to.equal(targetColor) - } - Cypress.Commands.overwrite( - 'should', - (originalFn, subject, expectation, ...args) => { - const customMatchers: { [key: string]: any } = { - 'have.backgroundColor': compareColor(args[0], 'backgroundColor'), - 'have.color': compareColor(args[0], 'color'), - } - - // See if the expectation is a string and if it is a member of Jest's expect - if (typeof expectation === 'string' && customMatchers[expectation]) { - return originalFn(subject, customMatchers[expectation]) - } - return originalFn(subject, expectation, ...args) - } - ) - const slateEditor = '[data-slate-node="element"]' - beforeEach(() => cy.visit('examples/richtext')) - it('select the correct block when triple clicking', () => { - // triple clicking the second block (paragraph) shouldn't highlight the - // quote button - for (let i = 0; i < 3; i++) { - cy.get(slateEditor) - .eq(1) - .click() - } - cy.contains('.material-icons', /format_quote/) - .parent() - .should('have.color', '#ccc') - }) -}) diff --git a/cypress/integration/examples/shadow-dom.ts b/cypress/integration/examples/shadow-dom.ts deleted file mode 100644 index 697c63ad04..0000000000 --- a/cypress/integration/examples/shadow-dom.ts +++ /dev/null @@ -1,10 +0,0 @@ -describe('shadow-dom example', () => { - beforeEach(() => cy.visit('examples/shadow-dom')) - - it('renders slate editor inside nested shadow', () => { - const outerShadow = cy.dataCy('outer-shadow-root').shadow() - const innerShadow = outerShadow.find('> div').shadow() - - innerShadow.findByRole('textbox').should('exist') - }) -}) diff --git a/cypress/integration/examples/tables.ts b/cypress/integration/examples/tables.ts deleted file mode 100644 index bd1482b9ad..0000000000 --- a/cypress/integration/examples/tables.ts +++ /dev/null @@ -1,11 +0,0 @@ -describe('table example', () => { - beforeEach(() => { - cy.visit('examples/tables') - }) - - it('table tag rendered', () => { - cy.findByRole('textbox') - .get('table') - .should('exist') - }) -}) diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts deleted file mode 100644 index b17c862685..0000000000 --- a/cypress/support/commands.ts +++ /dev/null @@ -1,15 +0,0 @@ -import '@testing-library/cypress/add-commands' - -declare global { - namespace Cypress { - interface Chainable { - /** - * Custom command to select DOM element by data-cy attribute. - * @example cy.dataCy('greeting') - */ - dataCy(value: string): Chainable - } - } -} - -Cypress.Commands.add('dataCy', value => cy.get(`[data-cy="${value}"]`)) diff --git a/cypress/support/index.ts b/cypress/support/index.ts deleted file mode 100644 index 43c03b759d..0000000000 --- a/cypress/support/index.ts +++ /dev/null @@ -1 +0,0 @@ -import './commands' diff --git a/package.json b/package.json index 9b229e3ec4..8fbc8f92b0 100644 --- a/package.json +++ b/package.json @@ -31,12 +31,11 @@ "test": "yarn run test:mocha && yarn run test:jest", "test:custom": "mocha --require ./config/babel/register.cjs ./packages/slate/test/index.js", "test:inspect": "yarn test --inspect-brk", - "test:integration": "run-p -r serve cypress:run", + "test:integration": "run-p -r serve playwright", "test:mocha": "mocha --require ./config/babel/register.cjs ./packages/{slate,slate-history,slate-hyperscript}/test/**/*.{js,ts}", "test:jest": "jest --config jest.config.js", "watch": "yarn build:rollup --watch", - "cypress:run": "cypress run", - "cypress:open": "cypress open" + "playwright": "playwright test" }, "devDependencies": { "@babel/cli": "^7.7.4", @@ -55,6 +54,7 @@ "@changesets/changelog-github": "^0.3.0", "@changesets/cli": "^2.14.1", "@emotion/css": "^11.7.1", + "@playwright/test": "^1.29.1", "@testing-library/cypress": "^8.0.0", "@types/jest": "27.0.1", "@types/lodash": "^4.14.149", @@ -68,7 +68,6 @@ "babel-jest": "27.0.6", "babel-plugin-dev-expression": "^0.2.2", "babel-plugin-module-resolver": "^3.1.1", - "cypress": "^8.3.0", "eslint": "^7.32.0", "eslint-config-prettier": "^7.2.0", "eslint-plugin-import": "^2.18.2", @@ -85,6 +84,7 @@ "mocha": "^6.2.0", "next": "^12.2.0", "npm-run-all": "^4.1.2", + "playwright": "^1.29.1", "prettier": "^1.19.1", "prismjs": "^1.5.1", "react": "^16.12.0", diff --git a/packages/slate-react/test/index.spec.tsx b/packages/slate-react/test/index.spec.tsx index a226ea173a..c482d1c2ed 100644 --- a/packages/slate-react/test/index.spec.tsx +++ b/packages/slate-react/test/index.spec.tsx @@ -19,7 +19,7 @@ describe('slate-react', () => { describe('Editable', () => { describe('NODE_TO_KEY logic', () => { - it('should not unmount the node that gets split on a split_node operation', async () => { + test('should not unmount the node that gets split on a split_node operation', async () => { const editor = withReact(createEditor()) const value = [{ type: 'block', children: [{ text: 'test' }] }] const mounts = jest.fn() @@ -50,7 +50,7 @@ describe('slate-react', () => { expect(mounts).toHaveBeenCalledTimes(2) }) - it('should not unmount the node that gets merged into on a merge_node operation', async () => { + test('should not unmount the node that gets merged into on a merge_node operation', async () => { const editor = withReact(createEditor()) const value = [ { type: 'block', children: [{ text: 'te' }] }, diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000000..255330a017 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,90 @@ +import { PlaywrightTestConfig } from '@playwright/test' +import { devices } from '@playwright/test' +import os from 'os' + +const projects = [ + { + name: 'chromium', + use: { + ...devices['Desktop Chrome'], + permissions: ['clipboard-read', 'clipboard-write'], + launchOptions: { + // headless: false, + /** + * Enable scrollbars in headless mode. + */ + ignoreDefaultArgs: ['--hide-scrollbars'], + }, + }, + }, + { + name: 'firefox', + use: { + ...devices['Desktop Firefox'], + }, + }, +] + +if (os.type() === 'Darwin') { + projects.push({ + name: 'webkit', + use: { + ...devices['Desktop Safari'], + }, + }) +} + +const retries = process.env.PLAYWRIGHT_RETRIES + ? +process.env.PLAYWRIGHT_RETRIES + : process.env.CI + ? 5 + : 2 + +/** + * See https://playwright.dev/docs/test-configuration. + */ +const config: PlaywrightTestConfig = { + testDir: './playwright', + /* Maximum time one test can run for. */ + timeout: 10 * 1000, + expect: { + /** + * Maximum time expect() should wait for the condition to be met. + * For example in `await expect(locator).toHaveText();` + */ + timeout: 8000, + }, + /* Run tests in files in parallel */ + fullyParallel: !process.env.CI, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + // allow PLAYWRIGHT_RETRIES to override for local dev + retries, + /* Opt out of parallel tests. */ + // workers: 1, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: process.env.CI ? 'github' : 'list', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + viewport: { + width: 1280, + height: 720, + }, + /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ + actionTimeout: 0, + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://localhost:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects, + + /* Folder for test artifacts such as screenshots, videos, traces, etc. */ + // outputDir: 'test-results/', +} + +export default config diff --git a/playwright/integration/examples/check-lists.test.ts b/playwright/integration/examples/check-lists.test.ts new file mode 100644 index 0000000000..ce6ccb6fb3 --- /dev/null +++ b/playwright/integration/examples/check-lists.test.ts @@ -0,0 +1,50 @@ +import { test, expect } from '@playwright/test' + +test.describe('Check-lists example', () => { + test.beforeEach(async ({ page }) => { + await page.goto('http://localhost:3000/examples/check-lists') + }) + + test('checks the bullet when clicked', async ({ page }) => { + const slateNodeElement = 'div[data-slate-node="element"]' + + expect( + await page + .locator(slateNodeElement) + .nth(3) + .textContent() + ).toBe('Criss-cross!') + + await expect( + page + .locator(slateNodeElement) + .nth(3) + .locator('span') + .nth(1) + ).toHaveCSS('text-decoration-line', 'line-through') + + // Unchecking the checkboxes should un-cross the corresponding text. + await page + .locator(slateNodeElement) + .nth(3) + .locator('span') + .nth(0) + .locator('input') + .uncheck() + expect( + await page + .locator(slateNodeElement) + .nth(3) + .textContent() + ).toBe('Criss-cross!') + await expect( + page + .locator(slateNodeElement) + .nth(3) + .locator('span') + .nth(1) + ).toHaveCSS('text-decoration-line', 'none') + + expect(await page.locator('p[data-slate-node="element"]').count()).toBe(2) + }) +}) diff --git a/playwright/integration/examples/code-highlighting.test.ts b/playwright/integration/examples/code-highlighting.test.ts new file mode 100644 index 0000000000..18285f8807 --- /dev/null +++ b/playwright/integration/examples/code-highlighting.test.ts @@ -0,0 +1,57 @@ +import { test, expect } from '@playwright/test' + +test.describe('code highlighting', () => { + const slateEditor = '[data-slate-node="element"]' + const leafNode = 'span[data-slate-leaf="true"]' + + test.beforeEach(async ({ page }) => { + page.goto('http://localhost:3000/examples/code-highlighting') + }) + + test('highlights HTML tags', async ({ page }) => { + const outer = page + .locator(slateEditor) + .locator('span') + .nth(0) + .locator(leafNode) + .nth(0) + await expect(await outer.textContent()).toContain('

') + await expect(outer).toHaveCSS('color', 'rgb(153, 0, 85)') + }) + + test('highlights javascript syntax', async ({ page }) => { + const JSCode = 'const slateVar = 30;' + await page.locator('select').selectOption('JavaScript') // Select the 'JavaScript' option + await expect(await page.locator('select').inputValue()).toBe('js') // Confirm value to avoid race condition + + await page.locator(slateEditor).click() // focus on the editor + const isMac = await page.evaluate(() => { + return /Mac|iPhone|iPod|iPad/i.test(navigator.platform) + }) + if (isMac) { + await page.keyboard.press('Meta+A') + } else { + await page.keyboard.press('Control+A') + } + await page.keyboard.type(JSCode) // Type JavaScript code + await page.keyboard.press('Enter') + + expect( + await page + .locator(slateEditor) + .locator('span') + .nth(0) + .locator(leafNode) + .nth(0) + .textContent() + ).toContain('const') + await expect( + page + .locator(slateEditor) + .locator('span') + .nth(0) + .locator(leafNode) + .nth(0) + ).toHaveCSS('color', 'rgb(0, 119, 170)') + }) +}) diff --git a/playwright/integration/examples/editable-voids.test.ts b/playwright/integration/examples/editable-voids.test.ts new file mode 100644 index 0000000000..abc88c1a7f --- /dev/null +++ b/playwright/integration/examples/editable-voids.test.ts @@ -0,0 +1,39 @@ +import { test, expect } from '@playwright/test' + +test.describe('editable voids', () => { + const input = 'input[type="text"]' + const elements = [ + { tag: 'h4', count: 3 }, + { tag: input, count: 1 }, + { tag: 'input[type="radio"]', count: 2 }, + ] + + test.beforeEach(async ({ page }) => { + await page.goto('http://localhost:3000/examples/editable-voids') + }) + + test('checks for the elements', async ({ page }) => { + for (const elem of elements) { + const { tag, count } = elem + expect(await page.locator(tag).count()).toBe(count) + } + }) + + test('should double the elements', async ({ page }) => { + // click the `+` sign to duplicate the editable void + await page + .locator('span.material-icons') + .nth(1) + .click() + + for (const elem of elements) { + const { tag, count } = elem + expect(await page.locator(tag).count()).toBe(count * 2) + } + }) + + test('make sure you can edit editable void', async ({ page }) => { + await page.locator(input).type('Typing') + expect(await page.locator(input).inputValue()).toBe('Typing') + }) +}) diff --git a/playwright/integration/examples/embeds.test.ts b/playwright/integration/examples/embeds.test.ts new file mode 100644 index 0000000000..adc1d12c7a --- /dev/null +++ b/playwright/integration/examples/embeds.test.ts @@ -0,0 +1,18 @@ +import { test, expect } from '@playwright/test' + +test.describe('embeds example', () => { + const slateEditor = 'div[data-slate-editor="true"]' + + test.beforeEach(async ({ page }) => { + await page.goto('http://localhost:3000/examples/embeds') + }) + + test('contains embeded', async ({ page }) => { + expect( + await page + .locator(slateEditor) + .locator('iframe') + .count() + ).toBe(1) + }) +}) diff --git a/playwright/integration/examples/forced-layout.test.ts b/playwright/integration/examples/forced-layout.test.ts new file mode 100644 index 0000000000..35b37fc915 --- /dev/null +++ b/playwright/integration/examples/forced-layout.test.ts @@ -0,0 +1,28 @@ +import { test, expect } from '@playwright/test' + +test.describe('forced layout example', () => { + const elements = [ + { tag: '#__next h2', count: 1 }, + { tag: '#__next p', count: 1 }, + ] + + test.beforeEach(async ({ page }) => { + await page.goto('http://localhost:3000/examples/forced-layout') + }) + + test('checks for the elements', async ({ page }) => { + for (const { tag, count } of elements) { + expect(await page.locator(tag).count()).toBe(count) + } + }) + + test('checks if elements persist even after everything is deleted', async ({ + page, + }) => { + // clear the textbox + await page.locator('div[role="textbox"]').clear() + for (const { tag, count } of elements) { + expect(await page.locator(tag).count()).toBe(count) + } + }) +}) diff --git a/playwright/integration/examples/hovering-toolbar.test.ts b/playwright/integration/examples/hovering-toolbar.test.ts new file mode 100644 index 0000000000..54e68d38e7 --- /dev/null +++ b/playwright/integration/examples/hovering-toolbar.test.ts @@ -0,0 +1,49 @@ +import { test, expect } from '@playwright/test' + +test.describe('hovering toolbar example', () => { + test.beforeEach(async ({ page }) => { + await page.goto('http://localhost:3000/examples/hovering-toolbar') + }) + + test('hovering toolbar appears', async ({ page }) => { + await page.pause() + await expect(page.locator('div').nth(12)).toHaveCSS('opacity', '0') + + await page + .locator('span[data-slate-string="true"]') + .nth(0) + .selectText() + expect( + await page + .locator('div') + .nth(12) + .count() + ).toBe(1) + + await expect(page.locator('div').nth(12)).toHaveCSS('opacity', '1') + expect( + await page + .locator('div') + .nth(12) + .locator('span.material-icons') + .count() + ).toBe(3) + }) + + test('hovering toolbar disappears', async ({ page }) => { + await page + .locator('span[data-slate-string="true"]') + .nth(0) + .selectText() + await expect(page.locator('div').nth(12)).toHaveCSS('opacity', '1') + await page + .locator('span[data-slate-string="true"]') + .nth(0) + .selectText() + await page + .locator('div') + .nth(0) + .click({ force: true }) + await expect(page.locator('div').nth(12)).toHaveCSS('opacity', '0') + }) +}) diff --git a/playwright/integration/examples/huge-document.test.ts b/playwright/integration/examples/huge-document.test.ts new file mode 100644 index 0000000000..d5fd4fcb29 --- /dev/null +++ b/playwright/integration/examples/huge-document.test.ts @@ -0,0 +1,18 @@ +import { test, expect } from '@playwright/test' + +test.describe('huge document example', () => { + const elements = [ + { tag: '#__next h1', count: 100 }, + { tag: '#__next p', count: 700 }, + ] + + test.beforeEach(async ({ page }) => { + await page.goto('http://localhost:3000/examples/huge-document') + }) + + test('contains image', async ({ page }) => { + for (const { tag, count } of elements) { + expect(await page.locator(tag).count()).toBe(count) + } + }) +}) diff --git a/playwright/integration/examples/iframe.test.ts b/playwright/integration/examples/iframe.test.ts new file mode 100644 index 0000000000..7f0a6ecf29 --- /dev/null +++ b/playwright/integration/examples/iframe.test.ts @@ -0,0 +1,24 @@ +import { test, expect, Page } from '@playwright/test' + +test.describe('iframe editor', () => { + test.beforeEach(async ({ page }) => { + await page.goto('http://localhost:3000/examples/iframe') + }) + + test('should be editable', async ({ page }) => { + await page + .frameLocator('iframe') + .locator('body') + .getByRole('textbox') + .focus() + await page.keyboard.press('Home') + await page.keyboard.type('Hello World') + expect( + await page + .frameLocator('iframe') + .locator('body') + .getByRole('textbox') + .textContent() + ).toContain('Hello World') + }) +}) diff --git a/playwright/integration/examples/images.test.ts b/playwright/integration/examples/images.test.ts new file mode 100644 index 0000000000..91eed527ab --- /dev/null +++ b/playwright/integration/examples/images.test.ts @@ -0,0 +1,16 @@ +import { test, expect } from '@playwright/test' + +test.describe('images example', () => { + test.beforeEach(async ({ page }) => { + await page.goto('http://localhost:3000/examples/images') + }) + + test('contains image', async ({ page }) => { + expect( + await page + .getByRole('textbox') + .locator('img') + .count() + ).toBe(2) + }) +}) diff --git a/playwright/integration/examples/inlines.test.ts b/playwright/integration/examples/inlines.test.ts new file mode 100644 index 0000000000..0abed686cc --- /dev/null +++ b/playwright/integration/examples/inlines.test.ts @@ -0,0 +1,17 @@ +import { test, expect } from '@playwright/test' + +test.describe('Inlines example', () => { + test.beforeEach(async ({ page }) => { + await page.goto('http://localhost:3000/examples/inlines') + }) + + test('contains link', async ({ page }) => { + expect( + await page + .getByRole('textbox') + .locator('a') + .nth(0) + .innerText() + ).toContain('hyperlink') + }) +}) diff --git a/playwright/integration/examples/markdown-preview.test.ts b/playwright/integration/examples/markdown-preview.test.ts new file mode 100644 index 0000000000..4fe1c16638 --- /dev/null +++ b/playwright/integration/examples/markdown-preview.test.ts @@ -0,0 +1,32 @@ +import { test, expect } from '@playwright/test' + +test.describe('markdown preview', () => { + const slateEditor = 'div[data-slate-editor="true"]' + const markdown = 'span[data-slate-string="true"]' + + test.beforeEach(async ({ page }) => { + await page.goto('http://localhost:3000/examples/markdown-preview') + }) + + test('checks for markdown', async ({ page }) => { + expect( + await page + .locator(slateEditor) + .locator(markdown) + .count() + ).toBe(9) + + await page.locator(slateEditor).click() + await page.keyboard.press('End') + await page.keyboard.press('Enter') + await page.keyboard.type('## Try it out!') + await page.keyboard.press('Enter') + await page.pause() + expect( + await page + .locator(slateEditor) + .locator(markdown) + .count() + ).toBe(10) + }) +}) diff --git a/playwright/integration/examples/markdown-shortcuts.test.ts b/playwright/integration/examples/markdown-shortcuts.test.ts new file mode 100644 index 0000000000..2d40acbe5c --- /dev/null +++ b/playwright/integration/examples/markdown-shortcuts.test.ts @@ -0,0 +1,78 @@ +import { test, expect } from '@playwright/test' + +test.describe('On markdown-shortcuts example', () => { + test.beforeEach(async ({ page }) => { + await page.goto('http://localhost:3000/examples/markdown-shortcuts') + }) + + test('contains quote', async ({ page }) => { + expect( + await page + .getByRole('textbox') + .locator('blockquote') + .textContent() + ).toContain('A wise quote.') + }) + + test('can add list items', async ({ page }) => { + expect( + await page + .getByRole('textbox') + .locator('ul') + .count() + ).toBe(0) + + await page.getByRole('textbox').click() + await page.getByRole('textbox').press('Home') + await page.getByRole('textbox').type('* 1st Item') + await page.keyboard.press('Enter') + await page.getByRole('textbox').type('2nd Item') + await page.keyboard.press('Enter') + await page.getByRole('textbox').type('3rd Item') + await page.keyboard.press('Enter') + await page.keyboard.press('Backspace') + + expect(await page.locator('ul > li').count()).toBe(3) + + expect( + await page + .locator('ul > li') + .nth(0) + .innerText() + ).toContain('1st Item') + expect( + await page + .locator('ul > li') + .nth(1) + .innerText() + ).toContain('2nd Item') + expect( + await page + .locator('ul > li') + .nth(2) + .innerText() + ).toContain('3rd Item') + }) + + test('can add a h1 item', async ({ page }) => { + expect( + await page + .getByRole('textbox') + .locator('h1') + .count() + ).toBe(0) + + await page.getByRole('textbox').press('Enter') + await page.getByRole('textbox').press('ArrowLeft') + await page.getByRole('textbox').type('# Heading') + + expect(await page.locator('h1').count()).toBe(1) + + expect( + await page + .getByRole('textbox') + .locator('h1') + .textContent() + ).toContain('Heading') + }) +}) diff --git a/playwright/integration/examples/mentions.test.ts b/playwright/integration/examples/mentions.test.ts new file mode 100644 index 0000000000..f2e23b70b8 --- /dev/null +++ b/playwright/integration/examples/mentions.test.ts @@ -0,0 +1,30 @@ +import { test, expect } from '@playwright/test' + +test.describe('mentions example', () => { + test.beforeEach( + async ({ page }) => + await page.goto('http://localhost:3000/examples/mentions') + ) + + test('renders mention element', async ({ page }) => { + expect(await page.locator('[data-cy="mention-R2-D2"]').count()).toBe(1) + expect(await page.locator('[data-cy="mention-Mace-Windu"]').count()).toBe(1) + }) + + test('shows list of mentions', async ({ page }) => { + await page.getByRole('textbox').click() + await page.getByRole('textbox').selectText() + await page.getByRole('textbox').press('Backspace') + await page.getByRole('textbox').type(' @ma') + expect(await page.locator('[data-cy="mentions-portal"]').count()).toBe(1) + }) + + test('inserts on enter from list', async ({ page }) => { + await page.getByRole('textbox').click() + await page.getByRole('textbox').selectText() + await page.getByRole('textbox').press('Backspace') + await page.getByRole('textbox').type(' @Ja') + await page.getByRole('textbox').press('Enter') + expect(await page.locator('[data-cy="mention-Jabba"]').count()).toBe(1) + }) +}) diff --git a/playwright/integration/examples/paste-html.test.ts b/playwright/integration/examples/paste-html.test.ts new file mode 100644 index 0000000000..d17e176fb5 --- /dev/null +++ b/playwright/integration/examples/paste-html.test.ts @@ -0,0 +1,38 @@ +import { test, expect, Page } from '@playwright/test' + +test.describe('paste html example', () => { + test.beforeEach( + async ({ page }) => + await page.goto('http://localhost:3000/examples/paste-html') + ) + + const pasteHtml = async (page: Page, htmlContent: string) => { + await page.getByRole('textbox').click() + await page.getByRole('textbox').selectText() + await page.keyboard.press('Backspace') + await page + .getByRole('textbox') + .evaluate((el: HTMLElement, htmlContent: string) => { + const clipboardEvent = Object.assign( + new Event('paste', { bubbles: true, cancelable: true }), + { + clipboardData: { + getData: (type = 'text/html') => htmlContent, + types: ['text/html'], + }, + } + ) + el.dispatchEvent(clipboardEvent) + }, htmlContent) + } + + test('pasted bold text uses ', async ({ page }) => { + await pasteHtml(page, 'Hello Bold') + expect(await page.locator('strong').textContent()).toContain('Hello') + }) + + test('pasted code uses ', async ({ page }) => { + await pasteHtml(page, 'console.log("hello from slate!")') + expect(await page.locator('code').textContent()).toContain('slate!') + }) +}) diff --git a/playwright/integration/examples/placeholder.test.ts b/playwright/integration/examples/placeholder.test.ts new file mode 100644 index 0000000000..4e87a2fcb6 --- /dev/null +++ b/playwright/integration/examples/placeholder.test.ts @@ -0,0 +1,17 @@ +import { test, expect } from '@playwright/test' + +test.describe('placeholder example', () => { + test.beforeEach( + async ({ page }) => + await page.goto('http://localhost:3000/examples/custom-placeholder') + ) + + test('renders custom placeholder', async ({ page }) => { + const placeholderElement = page.locator('[data-slate-placeholder=true]') + + expect(await placeholderElement.textContent()).toContain('Type something') + expect(await page.locator('pre').textContent()).toContain( + 'renderPlaceholder' + ) + }) +}) diff --git a/playwright/integration/examples/plaintext.test.ts b/playwright/integration/examples/plaintext.test.ts new file mode 100644 index 0000000000..efb9f4043e --- /dev/null +++ b/playwright/integration/examples/plaintext.test.ts @@ -0,0 +1,16 @@ +import { test, expect } from '@playwright/test' + +test.describe('plaintext example', () => { + test.beforeEach( + async ({ page }) => + await page.goto('http://localhost:3000/examples/plaintext') + ) + + test('inserts text when typed', async ({ page }) => { + await page.getByRole('textbox').press('Home') + await page.getByRole('textbox').type('Hello World') + expect(await page.getByRole('textbox').textContent()).toContain( + 'Hello World' + ) + }) +}) diff --git a/playwright/integration/examples/read-only.test.ts b/playwright/integration/examples/read-only.test.ts new file mode 100644 index 0000000000..601247bcda --- /dev/null +++ b/playwright/integration/examples/read-only.test.ts @@ -0,0 +1,17 @@ +import { test, expect } from '@playwright/test' + +test.describe('readonly editor', () => { + test.beforeEach(async ({ page }) => { + await page.goto('http://localhost:3000/examples/read-only') + }) + + test('should not be editable', async ({ page }) => { + const slateEditor = '[data-slate-editor="true"]' + expect( + await page.locator(slateEditor).getAttribute('contentEditable') + ).toBe('false') + expect(await page.locator(slateEditor).getAttribute('role')).toBe(null) + await page.locator(slateEditor).click() + await expect(page.locator(slateEditor)).not.toBeFocused() + }) +}) diff --git a/playwright/integration/examples/richtext.test.ts b/playwright/integration/examples/richtext.test.ts new file mode 100644 index 0000000000..e4ce632d72 --- /dev/null +++ b/playwright/integration/examples/richtext.test.ts @@ -0,0 +1,29 @@ +import { test, expect } from '@playwright/test' + +test.describe('On richtext example', () => { + test.beforeEach( + async ({ page }) => + await page.goto('http://localhost:3000/examples/richtext') + ) + + test('renders rich text', async ({ page }) => { + expect( + await page + .locator('strong') + .nth(0) + .textContent() + ).toContain('rich') + expect(await page.locator('blockquote').textContent()).toContain( + 'wise quote' + ) + }) + + test('inserts text when typed', async ({ page }) => { + await page.getByRole('textbox').press('Home') + await page.getByRole('textbox').type('Hello World') + + expect(await page.getByRole('textbox').textContent()).toContain( + 'Hello World' + ) + }) +}) diff --git a/playwright/integration/examples/search-highlighting.test.ts b/playwright/integration/examples/search-highlighting.test.ts new file mode 100644 index 0000000000..eabedb1a63 --- /dev/null +++ b/playwright/integration/examples/search-highlighting.test.ts @@ -0,0 +1,16 @@ +import { test, expect } from '@playwright/test' + +test.describe('search highlighting', () => { + test.beforeEach( + async ({ page }) => + await page.goto('http://localhost:3000/examples/search-highlighting') + ) + + test('highlights the searched text', async ({ page }) => { + const searchField = 'input[type="search"]' + const highlightedText = 'search-highlighted' + + await page.locator(searchField).type('text') + expect(await page.locator(`[data-cy="${highlightedText}"]`).count()).toBe(2) + }) +}) diff --git a/playwright/integration/examples/select.test.ts b/playwright/integration/examples/select.test.ts new file mode 100644 index 0000000000..8d1da0ebc0 --- /dev/null +++ b/playwright/integration/examples/select.test.ts @@ -0,0 +1,28 @@ +import { test, expect } from '@playwright/test' + +test.describe('selection', () => { + const slateEditor = '[data-slate-node="element"]' + test.beforeEach( + async ({ page }) => + await page.goto('http://localhost:3000/examples/richtext') + ) + test('select the correct block when triple clicking', async ({ page }) => { + // triple clicking the second block (paragraph) shouldn't highlight the + // quote button + for (let i = 0; i < 3; i++) { + await page + .locator(slateEditor) + .nth(1) + .click() + } + await page.pause() + + // .css-1vdn1ty is the gray, unselected button + expect( + await page + .locator('.css-1vdn1ty') + .nth(6) + .textContent() + ).toBe('format_quote') + }) +}) diff --git a/playwright/integration/examples/shadow-dom.test.ts b/playwright/integration/examples/shadow-dom.test.ts new file mode 100644 index 0000000000..63054afb0c --- /dev/null +++ b/playwright/integration/examples/shadow-dom.test.ts @@ -0,0 +1,15 @@ +import { test, expect } from '@playwright/test' + +test.describe('shadow-dom example', () => { + test.beforeEach( + async ({ page }) => + await page.goto('http://localhost:3000/examples/shadow-dom') + ) + + test('renders slate editor inside nested shadow', async ({ page }) => { + const outerShadow = page.locator('[data-cy="outer-shadow-root"]') + const innerShadow = outerShadow.locator('> div') + + expect(await innerShadow.getByRole('textbox').count()).toBe(1) + }) +}) diff --git a/playwright/integration/examples/tables.test.ts b/playwright/integration/examples/tables.test.ts new file mode 100644 index 0000000000..107a19b3c8 --- /dev/null +++ b/playwright/integration/examples/tables.test.ts @@ -0,0 +1,16 @@ +import { test, expect } from '@playwright/test' + +test.describe('table example', () => { + test.beforeEach(async ({ page }) => { + await page.goto('http://localhost:3000/examples/tables') + }) + + test('table tag rendered', async ({ page }) => { + expect( + await page + .getByRole('textbox') + .locator('table') + .count() + ).toBe(1) + }) +}) diff --git a/cypress/tsconfig.json b/playwright/tsconfig.json similarity index 96% rename from cypress/tsconfig.json rename to playwright/tsconfig.json index bdd03215c4..02ede46cec 100644 --- a/cypress/tsconfig.json +++ b/playwright/tsconfig.json @@ -16,7 +16,6 @@ "sourceMap": true /* Generates corresponding '.map' file. */, "strict": true /* Enable all strict type-checking options. */, "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, - "types": ["cypress", "@testing-library/cypress"], "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */, "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, "resolveJsonModule": true, diff --git a/yarn.lock b/yarn.lock index eac04a967f..663298006c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1744,44 +1744,6 @@ __metadata: languageName: node linkType: hard -"@cypress/request@npm:^2.88.5": - version: 2.88.5 - resolution: "@cypress/request@npm:2.88.5" - dependencies: - aws-sign2: ~0.7.0 - aws4: ^1.8.0 - caseless: ~0.12.0 - combined-stream: ~1.0.6 - extend: ~3.0.2 - forever-agent: ~0.6.1 - form-data: ~2.3.2 - har-validator: ~5.1.3 - http-signature: ~1.2.0 - is-typedarray: ~1.0.0 - isstream: ~0.1.2 - json-stringify-safe: ~5.0.1 - mime-types: ~2.1.19 - oauth-sign: ~0.9.0 - performance-now: ^2.1.0 - qs: ~6.5.2 - safe-buffer: ^5.1.2 - tough-cookie: ~2.5.0 - tunnel-agent: ^0.6.0 - uuid: ^3.3.2 - checksum: a605f8a623f4665402768f4d7730315a420967d41c44194eeb2a946ce0b74ce3eb8205a73b0cab879fcf65870dbb1189ac60ea67d163c7acd64228e39e65611a - languageName: node - linkType: hard - -"@cypress/xvfb@npm:^1.2.4": - version: 1.2.4 - resolution: "@cypress/xvfb@npm:1.2.4" - dependencies: - debug: ^3.1.0 - lodash.once: ^4.1.1 - checksum: 7bdcdaeb1bb692ec9d9bf8ec52538aa0bead6764753f4a067a171a511807a43fab016f7285a56bef6a606c2467ff3f1365e1ad2d2d583b81beed849ee1573fd1 - languageName: node - linkType: hard - "@emotion/babel-plugin@npm:^11.7.1": version: 11.7.2 resolution: "@emotion/babel-plugin@npm:11.7.2" @@ -3445,6 +3407,18 @@ __metadata: languageName: node linkType: hard +"@playwright/test@npm:^1.29.1": + version: 1.29.1 + resolution: "@playwright/test@npm:1.29.1" + dependencies: + "@types/node": "*" + playwright-core: 1.29.1 + bin: + playwright: cli.js + checksum: c4424ee09f6589a296a3a78402f8c9eac841f3e5f44d96e3a2ba07aaa00d53ee9c10cc96379b304743a9e70f231df0ded0ad2fc197f412ada67b4cf17ee24b0f + languageName: node + linkType: hard + "@rollup/pluginutils@npm:^3.1.0": version: 3.1.0 resolution: "@rollup/pluginutils@npm:3.1.0" @@ -3713,13 +3687,6 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^14.14.31": - version: 14.17.9 - resolution: "@types/node@npm:14.17.9" - checksum: e59b92e4346ed0db61e042d439f9658d1d3e8ad1d14825b714804cafae8ce22220ff6c8d907c4e4c6384aac748de07283fa321ef13cb8bdeb460eb789d634244 - languageName: node - linkType: hard - "@types/node@npm:^16.11.26": version: 16.11.26 resolution: "@types/node@npm:16.11.26" @@ -3821,20 +3788,6 @@ __metadata: languageName: node linkType: hard -"@types/sinonjs__fake-timers@npm:^6.0.2": - version: 6.0.3 - resolution: "@types/sinonjs__fake-timers@npm:6.0.3" - checksum: 6def7829e18b6f23506f7c4d6843e9ab80ae2ae983e2321c10fd816667297af519d96819aa436f30ae1a9a35b75bfbe4489f310cee1bf0983eede1ba3248b44c - languageName: node - linkType: hard - -"@types/sizzle@npm:^2.3.2": - version: 2.3.3 - resolution: "@types/sizzle@npm:2.3.3" - checksum: 586a9fb1f6ff3e325e0f2cc1596a460615f0bc8a28f6e276ac9b509401039dd242fa8b34496d3a30c52f5b495873922d09a9e76c50c2ab2bcc70ba3fb9c4e160 - languageName: node - linkType: hard - "@types/stack-utils@npm:^2.0.0": version: 2.0.1 resolution: "@types/stack-utils@npm:2.0.1" @@ -3865,15 +3818,6 @@ __metadata: languageName: node linkType: hard -"@types/yauzl@npm:^2.9.1": - version: 2.9.2 - resolution: "@types/yauzl@npm:2.9.2" - dependencies: - "@types/node": "*" - checksum: dfb49abe82605615712fc694eaa4f7068fe30aa03f38c085e2c2e74408beaad30471d36da9654a811482ece2ea4405575fd99b19c0aa327ed2a9736b554bbf43 - languageName: node - linkType: hard - "@typescript-eslint/eslint-plugin@npm:^5.30.5": version: 5.30.5 resolution: "@typescript-eslint/eslint-plugin@npm:5.30.5" @@ -4327,13 +4271,6 @@ __metadata: languageName: node linkType: hard -"arch@npm:^2.2.0": - version: 2.2.0 - resolution: "arch@npm:2.2.0" - checksum: e21b7635029fe8e9cdd5a026f9a6c659103e63fff423834323cdf836a1bb240a72d0c39ca8c470f84643385cf581bd8eda2cad8bf493e27e54bd9783abe9101f - languageName: node - linkType: hard - "are-we-there-yet@npm:~1.1.2": version: 1.1.5 resolution: "are-we-there-yet@npm:1.1.5" @@ -4534,13 +4471,6 @@ __metadata: languageName: node linkType: hard -"async@npm:^3.2.0": - version: 3.2.1 - resolution: "async@npm:3.2.1" - checksum: fcd6dc73d36bdb0e5bf978ea27832136cc8116cef15e0a2dc9a5c63f362c5f82363996a349bf3046d590587b7f30614ac340a4f3db6d48780a07f40f81941e0c - languageName: node - linkType: hard - "asynckit@npm:^0.4.0": version: 0.4.0 resolution: "asynckit@npm:0.4.0" @@ -4548,13 +4478,6 @@ __metadata: languageName: node linkType: hard -"at-least-node@npm:^1.0.0": - version: 1.0.0 - resolution: "at-least-node@npm:1.0.0" - checksum: 463e2f8e43384f1afb54bc68485c436d7622acec08b6fad269b421cb1d29cebb5af751426793d0961ed243146fe4dc983402f6d5a51b720b277818dbf6f2e49e - languageName: node - linkType: hard - "atob-lite@npm:^2.0.0": version: 2.0.0 resolution: "atob-lite@npm:2.0.0" @@ -4844,14 +4767,7 @@ __metadata: languageName: node linkType: hard -"blob-util@npm:^2.0.2": - version: 2.0.2 - resolution: "blob-util@npm:2.0.2" - checksum: d543e6b92e4ca715ca33c78e89a07a2290d43e5b2bc897d7ec588c5c7bbf59df93e45225ac0c9258aa6ce4320358990f99c9288f1c48280f8ec5d7a2e088d19b - languageName: node - linkType: hard - -"bluebird@npm:^3.5.1, bluebird@npm:^3.5.3, bluebird@npm:^3.5.5, bluebird@npm:^3.7.2": +"bluebird@npm:^3.5.1, bluebird@npm:^3.5.3, bluebird@npm:^3.5.5": version: 3.7.2 resolution: "bluebird@npm:3.7.2" checksum: 869417503c722e7dc54ca46715f70e15f4d9c602a423a02c825570862d12935be59ed9c7ba34a9b31f186c017c23cac6b54e35446f8353059c101da73eac22ef @@ -5069,13 +4985,6 @@ __metadata: languageName: node linkType: hard -"buffer-crc32@npm:~0.2.3": - version: 0.2.13 - resolution: "buffer-crc32@npm:0.2.13" - checksum: 06252347ae6daca3453b94e4b2f1d3754a3b146a111d81c68924c22d91889a40623264e95e67955b1cb4a68cbedf317abeabb5140a9766ed248973096db5ce1c - languageName: node - linkType: hard - "buffer-es6@npm:^4.9.2, buffer-es6@npm:^4.9.3": version: 4.9.3 resolution: "buffer-es6@npm:4.9.3" @@ -5190,13 +5099,6 @@ __metadata: languageName: node linkType: hard -"cachedir@npm:^2.3.0": - version: 2.3.0 - resolution: "cachedir@npm:2.3.0" - checksum: ec90cb0f2e6336e266aa748dbadf3da9e0b20e843e43f1591acab7a3f1451337dc2f26cb9dd833ae8cfefeffeeb43ef5b5ff62782a685f4e3c2305dd98482fcb - languageName: node - linkType: hard - "call-bind@npm:^1.0.0, call-bind@npm:^1.0.2": version: 1.0.2 resolution: "call-bind@npm:1.0.2" @@ -5372,13 +5274,6 @@ __metadata: languageName: node linkType: hard -"check-more-types@npm:^2.24.0": - version: 2.24.0 - resolution: "check-more-types@npm:2.24.0" - checksum: b09080ec3404d20a4b0ead828994b2e5913236ef44ed3033a27062af0004cf7d2091fbde4b396bf13b7ce02fb018bc9960b48305e6ab2304cd82d73ed7a51ef4 - languageName: node - linkType: hard - "chokidar@npm:^3.4.0": version: 3.5.2 resolution: "chokidar@npm:3.5.2" @@ -5494,20 +5389,6 @@ __metadata: languageName: node linkType: hard -"cli-table3@npm:~0.6.0": - version: 0.6.0 - resolution: "cli-table3@npm:0.6.0" - dependencies: - colors: ^1.1.2 - object-assign: ^4.1.0 - string-width: ^4.2.0 - dependenciesMeta: - colors: - optional: true - checksum: 98682a2d3eef5ad07d34a08f90398d0640004e28ecf8eb59006436f11ed7b4d453db09f46c2ea880618fbd61fee66321b3b3ee1b20276bc708b6baf6f9663d75 - languageName: node - linkType: hard - "cli-truncate@npm:^2.1.0": version: 2.1.0 resolution: "cli-truncate@npm:2.1.0" @@ -5653,13 +5534,6 @@ __metadata: languageName: node linkType: hard -"colors@npm:^1.1.2": - version: 1.4.0 - resolution: "colors@npm:1.4.0" - checksum: 98aa2c2418ad87dedf25d781be69dc5fc5908e279d9d30c34d8b702e586a0474605b3a189511482b9d5ed0d20c867515d22749537f7bc546256c6014f3ebdcec - languageName: node - linkType: hard - "columnify@npm:^1.5.4": version: 1.5.4 resolution: "columnify@npm:1.5.4" @@ -5693,13 +5567,6 @@ __metadata: languageName: node linkType: hard -"commander@npm:^5.1.0": - version: 5.1.0 - resolution: "commander@npm:5.1.0" - checksum: 0b7fec1712fbcc6230fcb161d8d73b4730fa91a21dc089515489402ad78810547683f058e2a9835929c212fead1d6a6ade70db28bbb03edbc2829a9ab7d69447 - languageName: node - linkType: hard - "commander@npm:^7.2.0": version: 7.2.0 resolution: "commander@npm:7.2.0" @@ -5707,13 +5574,6 @@ __metadata: languageName: node linkType: hard -"common-tags@npm:^1.8.0": - version: 1.8.0 - resolution: "common-tags@npm:1.8.0" - checksum: fb0cc9420d149176f2bd2b1fc9e6df622cd34eccaca60b276aa3253a7c9241e8a8ed1ec0702b2679eba7e47aeef721869c686bbd7257b75b5c44993c8462cd7f - languageName: node - linkType: hard - "commondir@npm:^1.0.1": version: 1.0.1 resolution: "commondir@npm:1.0.1" @@ -6056,7 +5916,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": +"cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" dependencies: @@ -6165,57 +6025,6 @@ __metadata: languageName: node linkType: hard -"cypress@npm:^8.3.0": - version: 8.3.0 - resolution: "cypress@npm:8.3.0" - dependencies: - "@cypress/request": ^2.88.5 - "@cypress/xvfb": ^1.2.4 - "@types/node": ^14.14.31 - "@types/sinonjs__fake-timers": ^6.0.2 - "@types/sizzle": ^2.3.2 - arch: ^2.2.0 - blob-util: ^2.0.2 - bluebird: ^3.7.2 - cachedir: ^2.3.0 - chalk: ^4.1.0 - check-more-types: ^2.24.0 - cli-cursor: ^3.1.0 - cli-table3: ~0.6.0 - commander: ^5.1.0 - common-tags: ^1.8.0 - dayjs: ^1.10.4 - debug: ^4.3.2 - enquirer: ^2.3.6 - eventemitter2: ^6.4.3 - execa: 4.1.0 - executable: ^4.1.1 - extract-zip: 2.0.1 - figures: ^3.2.0 - fs-extra: ^9.1.0 - getos: ^3.2.1 - is-ci: ^3.0.0 - is-installed-globally: ~0.4.0 - lazy-ass: ^1.6.0 - listr2: ^3.8.3 - lodash: ^4.17.21 - log-symbols: ^4.0.0 - minimist: ^1.2.5 - ospath: ^1.2.2 - pretty-bytes: ^5.6.0 - ramda: ~0.27.1 - request-progress: ^3.0.0 - supports-color: ^8.1.1 - tmp: ~0.2.1 - untildify: ^4.0.0 - url: ^0.11.0 - yauzl: ^2.10.0 - bin: - cypress: bin/cypress - checksum: acffc5ee3a347c3dc0989b43509f1d56ef2089aab6d8a2a8087f91dd93fd81d86939fa71a6df4a6690014385650e24599777bc5513c66ef7acfe34e6733dc4d6 - languageName: node - linkType: hard - "dargs@npm:^4.0.1": version: 4.1.0 resolution: "dargs@npm:4.1.0" @@ -6259,13 +6068,6 @@ __metadata: languageName: node linkType: hard -"dayjs@npm:^1.10.4": - version: 1.10.6 - resolution: "dayjs@npm:1.10.6" - checksum: a79af6d19cf1e3aefc231e480037b37df50701e8d0cdbe1736fc6665b7045a159777215c5c8dd42ede7dd3c4c4f4a174d4ddd5bffca51df2a4ba0e0e25a6163c - languageName: node - linkType: hard - "debug@npm:3.1.0": version: 3.1.0 resolution: "debug@npm:3.1.0" @@ -6284,7 +6086,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.0.1, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2": +"debug@npm:4, debug@npm:^4.0.1, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1": version: 4.3.2 resolution: "debug@npm:4.3.2" dependencies: @@ -7157,13 +6959,6 @@ __metadata: languageName: node linkType: hard -"eventemitter2@npm:^6.4.3": - version: 6.4.4 - resolution: "eventemitter2@npm:6.4.4" - checksum: b5e707039973d5a770bc4c64255604df66df3a1f63389dccb7118af163b9f790ca7596463d7868426339301ad9de5ef1c3f4a9c7ac3b93874c5ca792916dede1 - languageName: node - linkType: hard - "eventemitter3@npm:^3.1.0": version: 3.1.2 resolution: "eventemitter3@npm:3.1.2" @@ -7182,23 +6977,6 @@ __metadata: languageName: node linkType: hard -"execa@npm:4.1.0": - version: 4.1.0 - resolution: "execa@npm:4.1.0" - dependencies: - cross-spawn: ^7.0.0 - get-stream: ^5.0.0 - human-signals: ^1.1.1 - is-stream: ^2.0.0 - merge-stream: ^2.0.0 - npm-run-path: ^4.0.0 - onetime: ^5.1.0 - signal-exit: ^3.0.2 - strip-final-newline: ^2.0.0 - checksum: e30d298934d9c52f90f3847704fd8224e849a081ab2b517bbc02f5f7732c24e56a21f14cb96a08256deffeb2d12b2b7cb7e2b014a12fb36f8d3357e06417ed55 - languageName: node - linkType: hard - "execa@npm:^0.7.0": version: 0.7.0 resolution: "execa@npm:0.7.0" @@ -7246,15 +7024,6 @@ __metadata: languageName: node linkType: hard -"executable@npm:^4.1.1": - version: 4.1.1 - resolution: "executable@npm:4.1.1" - dependencies: - pify: ^2.2.0 - checksum: f01927ce59bccec804e171bf859a26e362c1f50aa9ebc69f7cafdcce3859d29d4b6267fd47237c18b0a1830614bd3f0ee14b7380d9bad18a4e7af9b5f0b6984f - languageName: node - linkType: hard - "exit@npm:^0.1.2": version: 0.1.2 resolution: "exit@npm:0.1.2" @@ -7351,23 +7120,6 @@ __metadata: languageName: node linkType: hard -"extract-zip@npm:2.0.1": - version: 2.0.1 - resolution: "extract-zip@npm:2.0.1" - dependencies: - "@types/yauzl": ^2.9.1 - debug: ^4.1.1 - get-stream: ^5.1.0 - yauzl: ^2.10.0 - dependenciesMeta: - "@types/yauzl": - optional: true - bin: - extract-zip: cli.js - checksum: 8cbda9debdd6d6980819cc69734d874ddd71051c9fe5bde1ef307ebcedfe949ba57b004894b585f758b7c9eeeea0e3d87f2dda89b7d25320459c2c9643ebb635 - languageName: node - linkType: hard - "extsprintf@npm:1.3.0": version: 1.3.0 resolution: "extsprintf@npm:1.3.0" @@ -7475,15 +7227,6 @@ __metadata: languageName: node linkType: hard -"fd-slicer@npm:~1.1.0": - version: 1.1.0 - resolution: "fd-slicer@npm:1.1.0" - dependencies: - pend: ~1.2.0 - checksum: c8585fd5713f4476eb8261150900d2cb7f6ff2d87f8feb306ccc8a1122efd152f1783bdb2b8dc891395744583436bfd8081d8e63ece0ec8687eeefea394d4ff2 - languageName: node - linkType: hard - "figgy-pudding@npm:^3.4.1, figgy-pudding@npm:^3.5.1": version: 3.5.2 resolution: "figgy-pudding@npm:3.5.2" @@ -7500,15 +7243,6 @@ __metadata: languageName: node linkType: hard -"figures@npm:^3.2.0": - version: 3.2.0 - resolution: "figures@npm:3.2.0" - dependencies: - escape-string-regexp: ^1.0.5 - checksum: 85a6ad29e9aca80b49b817e7c89ecc4716ff14e3779d9835af554db91bac41c0f289c418923519392a1e582b4d10482ad282021330cd045bb7b80c84152f2a2b - languageName: node - linkType: hard - "file-entry-cache@npm:^6.0.1": version: 6.0.1 resolution: "file-entry-cache@npm:6.0.1" @@ -7765,18 +7499,6 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:^9.1.0": - version: 9.1.0 - resolution: "fs-extra@npm:9.1.0" - dependencies: - at-least-node: ^1.0.0 - graceful-fs: ^4.2.0 - jsonfile: ^6.0.1 - universalify: ^2.0.0 - checksum: ba71ba32e0faa74ab931b7a0031d1523c66a73e225de7426e275e238e312d07313d2da2d33e34a52aa406c8763ade5712eb3ec9ba4d9edce652bcacdc29e6b20 - languageName: node - linkType: hard - "fs-minipass@npm:^1.2.7": version: 1.2.7 resolution: "fs-minipass@npm:1.2.7" @@ -7970,15 +7692,6 @@ __metadata: languageName: node linkType: hard -"get-stream@npm:^5.0.0, get-stream@npm:^5.1.0": - version: 5.2.0 - resolution: "get-stream@npm:5.2.0" - dependencies: - pump: ^3.0.0 - checksum: 8bc1a23174a06b2b4ce600df38d6c98d2ef6d84e020c1ddad632ad75bac4e092eeb40e4c09e0761c35fc2dbc5e7fff5dab5e763a383582c4a167dd69a905bd12 - languageName: node - linkType: hard - "get-stream@npm:^6.0.0": version: 6.0.1 resolution: "get-stream@npm:6.0.1" @@ -7993,15 +7706,6 @@ __metadata: languageName: node linkType: hard -"getos@npm:^3.2.1": - version: 3.2.1 - resolution: "getos@npm:3.2.1" - dependencies: - async: ^3.2.0 - checksum: 42fd78a66d47cebd3e09de5566cc0044e034b08f4a000a310dbd89a77b02c65d8f4002554bfa495ea5bdc4fa9d515f5ac785a7cc474ba45383cc697f865eeaf1 - languageName: node - linkType: hard - "getpass@npm:^0.1.1": version: 0.1.7 resolution: "getpass@npm:0.1.7" @@ -8130,15 +7834,6 @@ __metadata: languageName: node linkType: hard -"global-dirs@npm:^3.0.0": - version: 3.0.0 - resolution: "global-dirs@npm:3.0.0" - dependencies: - ini: 2.0.0 - checksum: 953c17cf14bf6ee0e2100ae82a0d779934eed8a3ec5c94a7a4f37c5b3b592c31ea015fb9a15cf32484de13c79f4a814f3015152f3e1d65976cfbe47c1bfe4a88 - languageName: node - linkType: hard - "globals@npm:^11.1.0": version: 11.12.0 resolution: "globals@npm:11.12.0" @@ -8530,13 +8225,6 @@ __metadata: languageName: node linkType: hard -"human-signals@npm:^1.1.1": - version: 1.1.1 - resolution: "human-signals@npm:1.1.1" - checksum: d587647c9e8ec24e02821b6be7de5a0fc37f591f6c4e319b3054b43fd4c35a70a94c46fc74d8c1a43c47fde157d23acd7421f375e1c1365b09a16835b8300205 - languageName: node - linkType: hard - "human-signals@npm:^2.1.0": version: 2.1.0 resolution: "human-signals@npm:2.1.0" @@ -8734,13 +8422,6 @@ __metadata: languageName: node linkType: hard -"ini@npm:2.0.0": - version: 2.0.0 - resolution: "ini@npm:2.0.0" - checksum: e7aadc5fb2e4aefc666d74ee2160c073995a4061556b1b5b4241ecb19ad609243b9cceafe91bae49c219519394bbd31512516cb22a3b1ca6e66d869e0447e84e - languageName: node - linkType: hard - "ini@npm:^1.3.2, ini@npm:^1.3.4": version: 1.3.8 resolution: "ini@npm:1.3.8" @@ -9067,16 +8748,6 @@ __metadata: languageName: node linkType: hard -"is-installed-globally@npm:~0.4.0": - version: 0.4.0 - resolution: "is-installed-globally@npm:0.4.0" - dependencies: - global-dirs: ^3.0.0 - is-path-inside: ^3.0.2 - checksum: 3359840d5982d22e9b350034237b2cda2a12bac1b48a721912e1ab8e0631dd07d45a2797a120b7b87552759a65ba03e819f1bd63f2d7ab8657ec0b44ee0bf399 - languageName: node - linkType: hard - "is-lambda@npm:^1.0.1": version: 1.0.1 resolution: "is-lambda@npm:1.0.1" @@ -9144,13 +8815,6 @@ __metadata: languageName: node linkType: hard -"is-path-inside@npm:^3.0.2": - version: 3.0.3 - resolution: "is-path-inside@npm:3.0.3" - checksum: abd50f06186a052b349c15e55b182326f1936c89a78bf6c8f2b707412517c097ce04bc49a0ca221787bc44e1049f51f09a2ffb63d22899051988d3a618ba13e9 - languageName: node - linkType: hard - "is-plain-obj@npm:^1.0.0, is-plain-obj@npm:^1.1.0": version: 1.1.0 resolution: "is-plain-obj@npm:1.1.0" @@ -10147,19 +9811,6 @@ __metadata: languageName: node linkType: hard -"jsonfile@npm:^6.0.1": - version: 6.1.0 - resolution: "jsonfile@npm:6.1.0" - dependencies: - graceful-fs: ^4.1.6 - universalify: ^2.0.0 - dependenciesMeta: - graceful-fs: - optional: true - checksum: 7af3b8e1ac8fe7f1eccc6263c6ca14e1966fcbc74b618d3c78a0a2075579487547b94f72b7a1114e844a1e15bb00d440e5d1720bfc4612d790a6f285d5ea8354 - languageName: node - linkType: hard - "jsonparse@npm:^1.2.0": version: 1.3.1 resolution: "jsonparse@npm:1.3.1" @@ -10228,13 +9879,6 @@ __metadata: languageName: node linkType: hard -"lazy-ass@npm:^1.6.0": - version: 1.6.0 - resolution: "lazy-ass@npm:1.6.0" - checksum: 5a3ebb17915b03452320804466345382a6c25ac782ec4874fecdb2385793896cd459be2f187dc7def8899180c32ee0ab9a1aa7fe52193ac3ff3fe29bb0591729 - languageName: node - linkType: hard - "lerna@npm:^3.19.0": version: 3.22.1 resolution: "lerna@npm:3.22.1" @@ -10424,7 +10068,7 @@ __metadata: languageName: node linkType: hard -"listr2@npm:^3.8.2, listr2@npm:^3.8.3": +"listr2@npm:^3.8.2": version: 3.11.0 resolution: "listr2@npm:3.11.0" dependencies: @@ -10578,13 +10222,6 @@ __metadata: languageName: node linkType: hard -"lodash.once@npm:^4.1.1": - version: 4.1.1 - resolution: "lodash.once@npm:4.1.1" - checksum: d768fa9f9b4e1dc6453be99b753906f58990e0c45e7b2ca5a3b40a33111e5d17f6edf2f768786e2716af90a8e78f8f91431ab8435f761fef00f9b0c256f6d245 - languageName: node - linkType: hard - "lodash.set@npm:^4.3.2": version: 4.3.2 resolution: "lodash.set@npm:4.3.2" @@ -10655,7 +10292,7 @@ __metadata: languageName: node linkType: hard -"log-symbols@npm:^4.0.0, log-symbols@npm:^4.1.0": +"log-symbols@npm:^4.1.0": version: 4.1.0 resolution: "log-symbols@npm:4.1.0" dependencies: @@ -11797,7 +11434,7 @@ __metadata: languageName: node linkType: hard -"npm-run-path@npm:^4.0.0, npm-run-path@npm:^4.0.1": +"npm-run-path@npm:^4.0.1": version: 4.0.1 resolution: "npm-run-path@npm:4.0.1" dependencies: @@ -12079,13 +11716,6 @@ __metadata: languageName: node linkType: hard -"ospath@npm:^1.2.2": - version: 1.2.2 - resolution: "ospath@npm:1.2.2" - checksum: 505f48a4f4f1c557d6c656ec985707726e3714721680139be037613e903aa8c8fa4ddd8d1342006f9b2dc0065e6e20f8b7bea2ee05354f31257044790367b347 - languageName: node - linkType: hard - "outdent@npm:^0.5.0": version: 0.5.0 resolution: "outdent@npm:0.5.0" @@ -12466,13 +12096,6 @@ __metadata: languageName: node linkType: hard -"pend@npm:~1.2.0": - version: 1.2.0 - resolution: "pend@npm:1.2.0" - checksum: 6c72f5243303d9c60bd98e6446ba7d30ae29e3d56fdb6fae8767e8ba6386f33ee284c97efe3230a0d0217e2b1723b8ab490b1bbf34fcbb2180dbc8a9de47850d - languageName: node - linkType: hard - "performance-now@npm:^2.1.0": version: 2.1.0 resolution: "performance-now@npm:2.1.0" @@ -12503,7 +12126,7 @@ __metadata: languageName: node linkType: hard -"pify@npm:^2.0.0, pify@npm:^2.2.0, pify@npm:^2.3.0": +"pify@npm:^2.0.0, pify@npm:^2.3.0": version: 2.3.0 resolution: "pify@npm:2.3.0" checksum: 9503aaeaf4577acc58642ad1d25c45c6d90288596238fb68f82811c08104c800e5a7870398e9f015d82b44ecbcbef3dc3d4251a1cbb582f6e5959fe09884b2ba @@ -12585,6 +12208,26 @@ __metadata: languageName: node linkType: hard +"playwright-core@npm:1.29.1": + version: 1.29.1 + resolution: "playwright-core@npm:1.29.1" + bin: + playwright: cli.js + checksum: e1c8423db4d28f44e5365a353f321b04a07709c8bf26d0c908f06d2c2669f715315170f84ca98a532951c2ac6324f5f48f651dca1abe43092c7637bdb4703303 + languageName: node + linkType: hard + +"playwright@npm:^1.29.1": + version: 1.29.1 + resolution: "playwright@npm:1.29.1" + dependencies: + playwright-core: 1.29.1 + bin: + playwright: cli.js + checksum: ef20f71b20420d9e0eaf45ac5f936422dc6961a863eb71192abe911d3f951722b3c464a36ea04cac8484368c876b5513db04171375f970831fb2bccb9fbb1b7b + languageName: node + linkType: hard + "please-upgrade-node@npm:^3.2.0": version: 3.2.0 resolution: "please-upgrade-node@npm:3.2.0" @@ -12656,13 +12299,6 @@ __metadata: languageName: node linkType: hard -"pretty-bytes@npm:^5.6.0": - version: 5.6.0 - resolution: "pretty-bytes@npm:5.6.0" - checksum: 9c082500d1e93434b5b291bd651662936b8bd6204ec9fa17d563116a192d6d86b98f6d328526b4e8d783c07d5499e2614a807520249692da9ec81564b2f439cd - languageName: node - linkType: hard - "pretty-format@npm:^27.0.0, pretty-format@npm:^27.1.0": version: 27.1.0 resolution: "pretty-format@npm:27.1.0" @@ -12879,13 +12515,6 @@ __metadata: languageName: node linkType: hard -"punycode@npm:1.3.2": - version: 1.3.2 - resolution: "punycode@npm:1.3.2" - checksum: b8807fd594b1db33335692d1f03e8beeddde6fda7fbb4a2e32925d88d20a3aa4cd8dcc0c109ccaccbd2ba761c208dfaaada83007087ea8bfb0129c9ef1b99ed6 - languageName: node - linkType: hard - "punycode@npm:^2.1.0, punycode@npm:^2.1.1": version: 2.1.1 resolution: "punycode@npm:2.1.1" @@ -12928,13 +12557,6 @@ __metadata: languageName: node linkType: hard -"querystring@npm:0.2.0": - version: 0.2.0 - resolution: "querystring@npm:0.2.0" - checksum: 8258d6734f19be27e93f601758858c299bdebe71147909e367101ba459b95446fbe5b975bf9beb76390156a592b6f4ac3a68b6087cea165c259705b8b4e56a69 - languageName: node - linkType: hard - "queue-microtask@npm:^1.2.2": version: 1.2.3 resolution: "queue-microtask@npm:1.2.3" @@ -12956,13 +12578,6 @@ __metadata: languageName: node linkType: hard -"ramda@npm:~0.27.1": - version: 0.27.1 - resolution: "ramda@npm:0.27.1" - checksum: 31a0c0ef739b2525d7615f84cbb5d3cb89ee0c795469b711f729ea1d8df0dccc3cd75d3717a1e9742d42315ce86435680b7c87743eb7618111c60c144a5b8059 - languageName: node - linkType: hard - "randombytes@npm:^2.0.0, randombytes@npm:^2.0.1, randombytes@npm:^2.0.5, randombytes@npm:^2.1.0": version: 2.1.0 resolution: "randombytes@npm:2.1.0" @@ -13477,15 +13092,6 @@ __metadata: languageName: node linkType: hard -"request-progress@npm:^3.0.0": - version: 3.0.0 - resolution: "request-progress@npm:3.0.0" - dependencies: - throttleit: ^1.0.0 - checksum: 6ea1761dcc8a8b7b5894afd478c0286aa31bd69438d7050294bd4fd0d0b3e09b5cde417d38deef9c49809039c337d8744e4bb49d8632b0c3e4ffa5e8a687e0fd - languageName: node - linkType: hard - "request@npm:^2.88.0": version: 2.88.2 resolution: "request@npm:2.88.2" @@ -14232,6 +13838,7 @@ resolve@^2.0.0-next.3: "@changesets/changelog-github": ^0.3.0 "@changesets/cli": ^2.14.1 "@emotion/css": ^11.7.1 + "@playwright/test": ^1.29.1 "@testing-library/cypress": ^8.0.0 "@types/jest": 27.0.1 "@types/lodash": ^4.14.149 @@ -14245,7 +13852,6 @@ resolve@^2.0.0-next.3: babel-jest: 27.0.6 babel-plugin-dev-expression: ^0.2.2 babel-plugin-module-resolver: ^3.1.1 - cypress: ^8.3.0 eslint: ^7.32.0 eslint-config-prettier: ^7.2.0 eslint-plugin-import: ^2.18.2 @@ -14262,6 +13868,7 @@ resolve@^2.0.0-next.3: mocha: ^6.2.0 next: ^12.2.0 npm-run-all: ^4.1.2 + playwright: ^1.29.1 prettier: ^1.19.1 prismjs: ^1.5.1 react: ^16.12.0 @@ -15093,7 +14700,7 @@ resolve@^2.0.0-next.3: languageName: node linkType: hard -"supports-color@npm:^8.0.0, supports-color@npm:^8.1.1": +"supports-color@npm:^8.0.0": version: 8.1.1 resolution: "supports-color@npm:8.1.1" dependencies: @@ -15272,13 +14879,6 @@ resolve@^2.0.0-next.3: languageName: node linkType: hard -"throttleit@npm:^1.0.0": - version: 1.0.0 - resolution: "throttleit@npm:1.0.0" - checksum: 1b2db4d2454202d589e8236c07a69d2fab838876d370030ebea237c34c0a7d1d9cf11c29f994531ebb00efd31e9728291042b7754f2798a8352ec4463455b659 - languageName: node - linkType: hard - "through2@npm:^2.0.0, through2@npm:^2.0.2": version: 2.0.5 resolution: "through2@npm:2.0.5" @@ -15345,15 +14945,6 @@ resolve@^2.0.0-next.3: languageName: node linkType: hard -"tmp@npm:~0.2.1": - version: 0.2.1 - resolution: "tmp@npm:0.2.1" - dependencies: - rimraf: ^3.0.0 - checksum: 8b1214654182575124498c87ca986ac53dc76ff36e8f0e0b67139a8d221eaecfdec108c0e6ec54d76f49f1f72ab9325500b246f562b926f85bcdfca8bf35df9e - languageName: node - linkType: hard - "tmpl@npm:1.0.x": version: 1.0.4 resolution: "tmpl@npm:1.0.4" @@ -15819,13 +15410,6 @@ typescript@4.0.5: languageName: node linkType: hard -"universalify@npm:^2.0.0": - version: 2.0.0 - resolution: "universalify@npm:2.0.0" - checksum: 2406a4edf4a8830aa6813278bab1f953a8e40f2f63a37873ffa9a3bc8f9745d06cc8e88f3572cb899b7e509013f7f6fcc3e37e8a6d914167a5381d8440518c44 - languageName: node - linkType: hard - "unset-value@npm:^1.0.0": version: 1.0.0 resolution: "unset-value@npm:1.0.0" @@ -15836,13 +15420,6 @@ typescript@4.0.5: languageName: node linkType: hard -"untildify@npm:^4.0.0": - version: 4.0.0 - resolution: "untildify@npm:4.0.0" - checksum: 39ced9c418a74f73f0a56e1ba4634b4d959422dff61f4c72a8e39f60b99380c1b45ed776fbaa0a4101b157e4310d873ad7d114e8534ca02609b4916bb4187fb9 - languageName: node - linkType: hard - "upath@npm:^1.1.1, upath@npm:^1.2.0": version: 1.2.0 resolution: "upath@npm:1.2.0" @@ -15866,16 +15443,6 @@ typescript@4.0.5: languageName: node linkType: hard -"url@npm:^0.11.0": - version: 0.11.0 - resolution: "url@npm:0.11.0" - dependencies: - punycode: 1.3.2 - querystring: 0.2.0 - checksum: 50d100d3dd2d98b9fe3ada48cadb0b08aa6be6d3ac64112b867b56b19be4bfcba03c2a9a0d7922bfd7ac17d4834e88537749fe182430dfd9b68e520175900d90 - languageName: node - linkType: hard - "use-sync-external-store@npm:1.1.0": version: 1.1.0 resolution: "use-sync-external-store@npm:1.1.0" @@ -16498,16 +16065,6 @@ typescript@4.0.5: languageName: node linkType: hard -"yauzl@npm:^2.10.0": - version: 2.10.0 - resolution: "yauzl@npm:2.10.0" - dependencies: - buffer-crc32: ~0.2.3 - fd-slicer: ~1.1.0 - checksum: 7f21fe0bbad6e2cb130044a5d1d0d5a0e5bf3d8d4f8c4e6ee12163ce798fee3de7388d22a7a0907f563ac5f9d40f8699a223d3d5c1718da90b0156da6904022b - languageName: node - linkType: hard - "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0"