From 4ce468725e03d8ab7f6dc0b8e8c42e8777941a26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9e?= Date: Wed, 12 Jun 2024 20:26:16 +0200 Subject: [PATCH] ci: test with react 19 (#263) * ci: test with react 19 * fail-fast:false * harness update for react 18/19 * pseudofill navigator * ua * very old react 16 * oldest react --- .babelrc.js | 3 +++ .eslintrc.js | 4 ++++ .github/workflows/ci.yml | 5 ++-- package.json | 1 + test/test.js | 26 ++++++++++----------- test/util/render.js | 49 ++++++++++++++++++++++++++++------------ 6 files changed, 58 insertions(+), 30 deletions(-) diff --git a/.babelrc.js b/.babelrc.js index 0075d94..a29ad66 100644 --- a/.babelrc.js +++ b/.babelrc.js @@ -10,4 +10,7 @@ module.exports = { }], '@babel/react', ], + plugins: TEST ? [ + 'dynamic-import-node', + ] : [], }; diff --git a/.eslintrc.js b/.eslintrc.js index eb15afb..2a91a70 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,5 +1,9 @@ module.exports = { extends: 'airbnb', + parserOptions: { + ecmaVersion: 2022, + sourceType: 'module', + }, rules: { // I disagree 'react/jsx-filename-extension': 'off', diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ef952a9..ba0b859 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,9 +36,10 @@ jobs: test: name: Tests strategy: + fail-fast: false matrix: - node-version: [14.x, 16.x, 17.x] - react-version: [17.x, 18.x] + node-version: [16.x, 18.x, 20.x] + react-version: [17.x, 18.x, beta] include: - node-version: 14.x react-version: 16.0.0 diff --git a/package.json b/package.json index b5ba8a6..0d93289 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "@babel/register": "^7.12.10", "@rollup/plugin-babel": "^6.0.0", "@u-wave/react-vimeo-example": "file:example", + "babel-plugin-dynamic-import-node": "^2.3.3", "cross-env": "^7.0.3", "eslint": "^8.2.0", "eslint-config-airbnb": "^19.0.0", diff --git a/test/test.js b/test/test.js index 7acde90..018f91d 100644 --- a/test/test.js +++ b/test/test.js @@ -4,7 +4,7 @@ import render from './util/render'; describe('Vimeo', () => { it('should create a Vimeo player when mounted', async () => { const onReady = createSpy(); - const { sdkMock, playerMock } = render({ + const { sdkMock, playerMock } = await render({ video: 169408731, onReady, }); @@ -16,14 +16,14 @@ describe('Vimeo', () => { }); it('should use `url` prop for full vimeo URLs', async () => { - const { sdkMock } = render({ video: 'https://vimeo.com/179290396' }); + const { sdkMock } = await render({ video: 'https://vimeo.com/179290396' }); expect(sdkMock).toHaveBeenCalled(); expect(sdkMock.calls[0].arguments[1]).toMatch({ url: 'https://vimeo.com/179290396' }); }); it('should all onError when `ready()` fails', async () => { const onError = createSpy(); - const { sdkMock } = render({ + const { sdkMock } = await render({ video: 404, shouldFail: true, onError, @@ -36,7 +36,7 @@ describe('Vimeo', () => { }); it('should load a different video when "video" prop changes', async () => { - const { sdkMock, playerMock, rerender } = render({ + const { sdkMock, playerMock, rerender } = await render({ video: 169408731, }); expect(sdkMock).toHaveBeenCalled(); @@ -49,7 +49,7 @@ describe('Vimeo', () => { }); it('should pause the video using the "paused" prop', async () => { - const { playerMock, rerender } = render({ + const { playerMock, rerender } = await render({ video: 169408731, autoplay: true, }); @@ -66,7 +66,7 @@ describe('Vimeo', () => { }); it('should set the volume using the "volume" prop', async () => { - const { playerMock, rerender } = render({ + const { playerMock, rerender } = await render({ video: 169408731, volume: 0.5, }); @@ -78,7 +78,7 @@ describe('Vimeo', () => { }); it('should set the start time using the "start" prop', async () => { - const { playerMock, rerender } = render({ + const { playerMock, rerender } = await render({ video: 169408731, start: 60, }); @@ -93,7 +93,7 @@ describe('Vimeo', () => { }); it('should set the player color using the "color" prop', async () => { - const { playerMock, sdkMock, rerender } = render({ + const { playerMock, sdkMock, rerender } = await render({ video: 169408731, color: '#0000ff', }); @@ -107,7 +107,7 @@ describe('Vimeo', () => { }); it('should set the looping flag using the "loop" prop', async () => { - const { playerMock, sdkMock, rerender } = render({ + const { playerMock, sdkMock, rerender } = await render({ video: 169408731, loop: false, }); @@ -121,7 +121,7 @@ describe('Vimeo', () => { }); it('should set the iframe width/height using the width/height props', async () => { - const { sdkMock, playerMock, rerender } = render({ + const { sdkMock, playerMock, rerender } = await render({ video: 169408731, width: 640, height: 320, @@ -141,7 +141,7 @@ describe('Vimeo', () => { }); it('should set the playback rate using the "playbackRate" props', async () => { - const { playerMock, rerender } = render({ + const { playerMock, rerender } = await render({ video: 169408731, playbackRate: 0.5, }); @@ -154,13 +154,13 @@ describe('Vimeo', () => { }); it('should destroy player when unmounting', async () => { - const { playerMock, unmount } = render({ + const { playerMock, unmount } = await render({ video: 169408731, width: 640, height: 320, }); - await unmount(); + unmount(); expect(playerMock.destroy).toHaveBeenCalled(); }); diff --git a/test/util/render.js b/test/util/render.js index f45161d..bef2c64 100644 --- a/test/util/render.js +++ b/test/util/render.js @@ -5,12 +5,21 @@ import React from 'react'; import ReactDOM from 'react-dom'; +import { act } from 'react-dom/test-utils'; import env from 'min-react-env'; import createVimeo from './createVimeo'; -Object.assign(global, env); +Object.assign(global, env, { + navigator: { userAgent: 'min-react-env' }, +}); -const render = (initialProps) => { +const reactMajor = parseInt((ReactDOM.version || '16').split('.')[0], 10); + +function noAct(fn) { + return fn(); +} + +async function render(initialProps) { const { Vimeo, sdkMock, playerMock } = createVimeo({ shouldFail: initialProps.shouldFail, }); @@ -37,24 +46,34 @@ const render = (initialProps) => { } const div = env.document.createElement('div'); - const container = new Promise((resolve) => { - // eslint-disable-next-line react/no-deprecated - ReactDOM.render(, div); + let root; + if (reactMajor >= 18) { + const { createRoot } = await import('react-dom/client'); + root = createRoot(div); + } else { + root = { + render(element) { + // eslint-disable-next-line react/no-deprecated + ReactDOM.render(element, div); + }, + unmount() { + // eslint-disable-next-line react/no-deprecated + ReactDOM.unmountComponentAtNode(div); + }, + }; + } + const container = await new Promise((resolve) => { + root.render(); }); function rerender(newProps) { - return container.then((wrapper) => ( - new Promise((resolve) => { - wrapper.setState({ props: newProps }, () => { - Promise.resolve().then(resolve); - }); - }) - )); + return (act || noAct)(async () => { + container.setState({ props: newProps }); + }); } function unmount() { - // eslint-disable-next-line react/no-deprecated - ReactDOM.unmountComponentAtNode(div); + root.unmount(); } return { @@ -64,6 +83,6 @@ const render = (initialProps) => { rerender, unmount, }; -}; +} export default render;