diff --git a/.eslintrc.js b/.eslintrc.js index acb8329..0e33112 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,6 +1,8 @@ module.exports = { + parser: 'vue-eslint-parser', parserOptions: { parser: '@typescript-eslint/parser', + sourceType: 'module', }, extends: [ './node_modules/kcd-scripts/eslint.js', @@ -8,7 +10,6 @@ module.exports = { 'plugin:testing-library/vue', 'prettier', ], - plugins: ['vue'], rules: { 'no-console': 'off', 'import/no-unresolved': 'off', diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index afb9a07..0ef1301 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -17,7 +17,7 @@ jobs: main: strategy: matrix: - node: [12, 14, 16] + node: [14, 16, 18] runs-on: ubuntu-latest steps: - name: ⬇️ Checkout repo @@ -56,7 +56,7 @@ jobs: - name: ⎔ Setup node uses: actions/setup-node@v2 with: - node-version: 14 + node-version: 16 - name: 📥 Download deps uses: bahmutov/npm-install@v1 diff --git a/babel.config.js b/babel.config.js index 424f157..31ac518 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,5 +1,11 @@ module.exports = { sourceType: 'module', + plugins: [ + // Fixes for loose issue from https://github.com/rails/webpacker/issues/3008 + ['@babel/plugin-proposal-class-properties', {loose: true}], + ['@babel/plugin-proposal-private-methods', {loose: true}], + ['@babel/plugin-proposal-private-property-in-object', {loose: true}], + ], presets: [ [ '@babel/preset-env', diff --git a/jest.config.js b/jest.config.js index 9d901b6..a440b66 100644 --- a/jest.config.js +++ b/jest.config.js @@ -3,12 +3,15 @@ const config = require('kcd-scripts/jest') module.exports = merge(config, { testEnvironment: 'jsdom', + testEnvironmentOptions: { + customExportConditions: ['node', 'node-addons'], + }, moduleFileExtensions: ['js', 'vue'], coverageDirectory: './coverage', collectCoverageFrom: ['**/src/**/*.js', '!**/src/__tests__/**'], transform: { '^.+\\.js$': '/node_modules/babel-jest', - '.*\\.(vue)$': '/node_modules/vue-jest', + '^.+\\.vue$': '@vue/vue3-jest', }, snapshotSerializers: ['/node_modules/jest-serializer-vue'], testPathIgnorePatterns: [ diff --git a/package.json b/package.json index 174cbf6..273f286 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "setup": "npm install && npm run validate -s" }, "engines": { - "node": ">=12" + "node": ">=14" }, "files": [ "types", @@ -43,39 +43,36 @@ "author": "Daniel Cook", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.15.4", - "@testing-library/dom": "^8.5.0", - "@vue/test-utils": "^2.0.0" + "@babel/runtime": "^7.21.0", + "@testing-library/dom": "^9.0.1", + "@vue/test-utils": "^2.3.1" }, "devDependencies": { - "@apollo/client": "^3.4.11", - "@babel/plugin-transform-runtime": "^7.15.0", - "@testing-library/jest-dom": "^5.14.1", - "@testing-library/user-event": "^13.2.1", - "@types/estree": "^0.0.50", - "@vue/apollo-composable": "^4.0.0-alpha.14", - "@vue/compiler-sfc": "^3.2.12", - "apollo-boost": "^0.4.9", - "axios": "^0.20.0", - "element-plus": "^1.3.0-beta.1", - "eslint-plugin-vue": "^8.2.0", - "graphql": "^15.5.3", - "graphql-tag": "^2.12.4", - "isomorphic-unfetch": "^3.1.0", - "jest-serializer-vue": "^2.0.2", - "kcd-scripts": "^10.0.0", + "@babel/plugin-transform-runtime": "^7.21.0", + "@element-plus/icons-vue": "^2.1.0", + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/user-event": "^14.4.3", + "@types/estree": "^1.0.0", + "@vue/compiler-sfc": "^3.2.47", + "@vue/server-renderer": "^3.2.47", + "@vue/vue3-jest": "^29.2.3", + "axios": "^1.3.4", + "element-plus": "^2.2.36", + "eslint-plugin-vue": "^9.9.0", + "isomorphic-unfetch": "^4.0.2", + "jest-environment-jsdom": "^29.5.0", + "jest-serializer-vue": "^3.1.0", + "kcd-scripts": "^13.0.0", "lodash.merge": "^4.6.2", - "msw": "^0.21.3", - "tsd": "^0.19.1", - "typescript": "^4.4.3", - "vee-validate": "^4.3.5", - "vue": "^3.2.12", - "vue-apollo": "^3.0.5", - "vue-i18n": "^9.2.0-beta.26", - "vue-jest": "^5.0.0-alpha.10", - "vue-router": "^4.0.3", - "vuetify": "^v3.0.0-alpha.12", - "vuex": "^4.0.0" + "msw": "^1.1.0", + "tsd": "^0.27.0", + "typescript": "^4.9.5", + "vee-validate": "^4.7.4", + "vue": "^3.2.47", + "vue-eslint-parser": "^9.1.0", + "vue-i18n": "^9.2.2", + "vue-router": "^4.1.6", + "vuex": "^4.1.0" }, "peerDependencies": { "@vue/compiler-sfc": ">= 3", diff --git a/src/__tests__/hello-world-debug.js b/src/__tests__/hello-world-debug.js index 554feb0..ab2adf4 100644 --- a/src/__tests__/hello-world-debug.js +++ b/src/__tests__/hello-world-debug.js @@ -1,4 +1,4 @@ -/* eslint-disable testing-library/no-debug */ +/* eslint-disable testing-library/no-debugging-utils */ import {render} from '..' import HelloWorld from './components/HelloWorld' @@ -67,7 +67,7 @@ test('allows same arguments as prettyDOM', () => { expect(console.log).toHaveBeenCalledTimes(1) expect(console.log.mock.calls[0]).toMatchInlineSnapshot(` - Array [ + [
..., ] diff --git a/src/__tests__/user-event.js b/src/__tests__/user-event.js index ed02bfb..5d38af1 100644 --- a/src/__tests__/user-event.js +++ b/src/__tests__/user-event.js @@ -18,22 +18,23 @@ test('User events in a form', async () => { review: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.', rating: '3', } + const user = userEvent.setup() const {getByText, getByLabelText, emitted} = render(Form) const submitButton = getByText('Submit') expect(submitButton).toBeDisabled() const titleInput = getByLabelText(/title of the movie/i) - userEvent.type(titleInput, fakeReview.title) + await user.type(titleInput, fakeReview.title) expect(titleInput).toHaveValue(fakeReview.title) const textArea = getByLabelText(/Your review/i) - userEvent.type(textArea, 'The t-rex went insane!') + await user.type(textArea, 'The t-rex went insane!') expect(textArea).toHaveValue('The t-rex went insane!') - userEvent.clear(textArea) + await user.clear(textArea) expect(textArea).toHaveValue('') - userEvent.type(textArea, fakeReview.review) + await user.type(textArea, fakeReview.review) expect(textArea).toHaveValue(fakeReview.review) const initialSelectedRating = getByLabelText(/Awful/i) @@ -41,32 +42,34 @@ test('User events in a form', async () => { expect(initialSelectedRating).toBeChecked() expect(wonderfulRadioInput).not.toBeChecked() - userEvent.click(wonderfulRadioInput) + await user.click(wonderfulRadioInput) expect(wonderfulRadioInput).toBeChecked() await waitFor(() => expect(initialSelectedRating).not.toBeChecked()) const recommendInput = getByLabelText(/Would you recommend this movie?/i) - userEvent.click(recommendInput) + await user.click(recommendInput) expect(recommendInput).toBeChecked() - userEvent.tab() + await user.tab() expect(submitButton).toHaveFocus() expect(submitButton).toBeEnabled() - userEvent.type(submitButton, '{enter}') + await user.type(submitButton, '{enter}') expect(emitted('submit')[0][0]).toMatchObject(fakeReview) expect(console.warn).not.toHaveBeenCalled() }) -test('selecting option with user events', () => { +test('selecting option with user events', async () => { + const user = userEvent.setup() const {getByDisplayValue} = render(Select) + const select = getByDisplayValue('Tyrannosaurus') expect(select).toHaveValue('dino1') - userEvent.selectOptions(select, 'dino2') + await user.selectOptions(select, 'dino2') expect(select).toHaveValue('dino2') - userEvent.selectOptions(select, 'dino3') + await user.selectOptions(select, 'dino3') expect(select).not.toHaveValue('dino2') expect(select).toHaveValue('dino3') }) diff --git a/src/__tests__/vue-apollo.js b/src/__tests__/vue-apollo.js deleted file mode 100644 index 6d29c51..0000000 --- a/src/__tests__/vue-apollo.js +++ /dev/null @@ -1,94 +0,0 @@ -import '@testing-library/jest-dom' -import fetch from 'isomorphic-unfetch' -import {DefaultApolloClient} from '@vue/apollo-composable' -import ApolloClient from 'apollo-boost' -import {setupServer} from 'msw/node' -import {graphql} from 'msw' -import {render, fireEvent, screen} from '..' -import Component from './components/VueApollo.vue' - -// Since vue-apollo doesn't provide a MockProvider for Vue, -// you need to use some kind of mocks for the queries. - -// We are using Mock Service Worker (aka MSW) library to declaratively mock API communication -// in your tests instead of stubbing window.fetch, or relying on third-party adapters. - -const server = setupServer( - ...[ - graphql.query('getUser', (req, res, ctx) => { - const {variables} = req - - if (variables.id !== '1') { - return res( - ctx.errors([ - { - message: 'User not found', - }, - ]), - ) - } - - return res( - ctx.data({ - user: { - id: 1, - email: 'alice@example.com', - __typename: 'User', - }, - }), - ) - }), - - graphql.mutation('updateUser', (req, res, ctx) => { - const {variables} = req - - return res( - ctx.data({ - updateUser: { - id: variables.input.id, - email: variables.input.email, - __typename: 'User', - }, - }), - ) - }), - ], -) - -beforeAll(() => server.listen()) -afterEach(() => server.resetHandlers()) -afterAll(() => server.close()) - -test('mocking queries and mutations', async () => { - const apolloClient = new ApolloClient({ - uri: 'http://localhost:3000', - fetch, - }) - - render(Component, { - props: {id: '1'}, - global: { - provide: { - [DefaultApolloClient]: apolloClient, - }, - }, - }) - - //Initial rendering will be in the loading state, - expect(screen.getByText('Loading')).toBeInTheDocument() - - expect( - await screen.findByText('Email: alice@example.com'), - ).toBeInTheDocument() - - await fireEvent.update( - screen.getByLabelText('Email'), - 'alice+new@example.com', - ) - - await fireEvent.click(screen.getByRole('button', {name: 'Change email'})) - - expect( - await screen.findByText('Email: alice+new@example.com'), - ).toBeInTheDocument() -}) diff --git a/src/__tests__/vuetify.js b/src/__tests__/vuetify.js deleted file mode 100644 index a933e8e..0000000 --- a/src/__tests__/vuetify.js +++ /dev/null @@ -1,73 +0,0 @@ -test.todo('Your test suite must contain at least one test.') -// import '@testing-library/jest-dom' -// import Vue from 'vue' -// import {render, fireEvent} from '..' -// import Vuetify from 'vuetify' -// import VuetifyDemoComponent from './components/Vuetify' - -// // We need to use a global Vue instance, otherwise Vuetify will complain about -// // read-only attributes. -// // This could also be done in a custom Jest-test-setup file to execute for all tests. -// // More info: https://github.com/vuetifyjs/vuetify/issues/4068 -// // https://vuetifyjs.com/en/getting-started/unit-testing -// Vue.use(Vuetify) - -// // Custom container to integrate Vuetify with Vue Testing Library. -// // Vuetify requires you to wrap your app with a v-app component that provides -// // a
node. -// const renderWithVuetify = (component, options, callback) => { -// const root = document.createElement('div') -// root.setAttribute('data-app', 'true') - -// return render( -// component, -// { -// container: document.body.appendChild(root), -// // for Vuetify components that use the $vuetify instance property -// vuetify: new Vuetify(), -// ...options, -// }, -// callback, -// ) -// } - -// test('should set [data-app] attribute on outer most div', () => { -// const {container} = renderWithVuetify(VuetifyDemoComponent) - -// expect(container).toHaveAttribute('data-app', 'true') -// }) - -// test('renders a Vuetify-powered component', async () => { -// const {getByText} = renderWithVuetify(VuetifyDemoComponent) - -// await fireEvent.click(getByText('open')) - -// expect(getByText('Lorem ipsum dolor sit amet.')).toMatchInlineSnapshot(` -//
-// Lorem ipsum dolor sit amet. -//
-// `) -// }) - -// test('opens a menu', async () => { -// const {getByRole, getByText, queryByText} = renderWithVuetify( -// VuetifyDemoComponent, -// ) - -// const openMenuButton = getByRole('button', {name: 'open menu'}) - -// // Menu item is not rendered initially -// expect(queryByText('menu item')).not.toBeInTheDocument() - -// await fireEvent.click(openMenuButton) - -// const menuItem = getByText('menu item') -// expect(menuItem).toBeInTheDocument() - -// await fireEvent.click(openMenuButton) - -// expect(menuItem).toBeInTheDocument() -// expect(menuItem).not.toBeVisible() -// }) diff --git a/types/index.test-d.ts b/types/index.test-d.ts index 09ad1fd..5a0253d 100644 --- a/types/index.test-d.ts +++ b/types/index.test-d.ts @@ -93,7 +93,7 @@ export function testEmitted() { eslint testing-library/prefer-explicit-assert: "off", testing-library/no-wait-for-empty-callback: "off", - testing-library/no-debug: "off", + testing-library/no-debugging-utils: "off", testing-library/prefer-screen-queries: "off", @typescript-eslint/unbound-method: "off", @typescript-eslint/no-invalid-void-type: "off"