From 831d2afc934c0905ffbe11ba2fcaa21276e13859 Mon Sep 17 00:00:00 2001 From: Ross MacPhee <10212476+rossyman@users.noreply.github.com> Date: Mon, 29 Mar 2021 22:40:31 +0100 Subject: [PATCH] Initialise Svelte Jest adder --- .gitignore | 79 ++------------- README.md | 84 +++++++++++++++- package.json | 25 +++++ preset.ts | 201 +++++++++++++++++++++++++++++++++++++ templates/.babelrc | 12 +++ templates/index.spec.js | 8 ++ templates/jest.config.json | 11 ++ 7 files changed, 345 insertions(+), 75 deletions(-) create mode 100644 package.json create mode 100644 preset.ts create mode 100644 templates/.babelrc create mode 100644 templates/index.spec.js create mode 100644 templates/jest.config.json diff --git a/.gitignore b/.gitignore index 6704566..8a97ee8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,9 +2,6 @@ logs *.log npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json @@ -15,34 +12,9 @@ pids *.seed *.pid.lock -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories +# Dependencies node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ +package-lock.json # TypeScript cache *.tsbuildinfo @@ -53,52 +25,13 @@ typings/ # Optional eslint cache .eslintcache -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env -.env.test - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# Next.js build output -.next - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and *not* Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port +# IDE Specific files +.idea/ +.vscode/ +.history/ diff --git a/README.md b/README.md index 1de74ba..4290911 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,82 @@ -# svelte-add-jest -SvelteKit adder for Jest unit testing +

Welcome to svelte-add-jest 👋

+

+ Version + + License: MIT + +

+ +## ❓ What is this? + +This is an **experimental** command to run to add Jest to your SvelteKit project. + +## 🛠 Usage + +You must start with a fresh copy of the official SvelteKit template, which is currently created by running this command: + +```sh +npm init svelte@next +``` + +Once that is set up, run this command in your project directory to set up Jest: + +```sh +npx svelte-add jest +``` + +After the preset runs, + +- You can apply _another_ [Svelte Adder](https://github.com/svelte-add/svelte-adders) to your project for more functionality. + +### ⚙️ Options + +| Description | Flag | Negated | Default | +|----------------------|-----------------------------|--------------------------------|-----------------| +| Interactive Mode | `--interaction` | `--no-interaction` | True | +| Jest DOM Support | `--jest-dom` | `--no-jest-dom` | True | +| Generate Example | `--examples` | `--no-examples` | True | + +## 👀 See Also + +- [Svelte Testing Library Docs](https://testing-library.com/docs/svelte-testing-library/intro/) +- [Jest DOM](https://github.com/testing-library/jest-dom#usage) +- [Jest](https://jestjs.io) + +## 🤝 Contributing + +Contributions, issues and feature requests are welcome!
Feel free to check [issues page](https://github.com/rossyman/svelte-add-jest/issues). + +### 😵 Help! I have a question + +[Create an issue](https://github.com/svelte-add/jest/issues/new) and we'll try to help. + +### 😡 Fix! There is something that needs improvement + +[Create an issue](https://github.com/rossyman/svelte-add-jest/issues/new) or [pull request](https://github.com/rossyman/svelte-add-jest/pulls) and we'll try to fix. + +These are new tools, so there are likely to be problems in this project. Thank you for bringing them to our attention or fixing them for us. + +## Show your support + +Give a ⭐️ if this project helped you! + +## Author + +👤 **Ross MacPhee** + +- Twitter: [@rossco___](https://twitter.com/rossco___) +- Github: [@rossyman](https://github.com/rossyman) +- LinkedIn: [@ross-macphee](https://linkedin.com/in/ross-macphee) + +👤 **Brady Wiggins** + +- Github: [@FractalHQ](https://github.com/FractalHQ) + +## 📝 License + +Copyright © 2021 - Ross MacPhee & Brady Wiggins.
+This project is [MIT](https://github.com/rossyman/svelte-add-jest/blob/main/LICENSE) licensed. + +--- + +_This README was generated with ❤️ by [readme-md-generator](https://github.com/kefranabg/readme-md-generator)_ diff --git a/package.json b/package.json new file mode 100644 index 0000000..a33c365 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "private": true, + "name": "@rossyman/svelte-add-jest", + "version": "1.0.0", + "description": "SvelteKit adder for Jest unit testing", + "license": "MIT", + "keywords": [ + "svelte", + "sveltekit", + "svelte-kit", + "jest", + "unit-test", + "test" + ], + "repository": "github:rossyman/svelte-add-jest", + "bugs": "https://github.com/rossyman/svelte-add-jest/issues", + "contributors": [ + "Ross MacPhee (https://github.com/rossyman)", + "Brady Wiggins (https://github.com/FractalHQ)" + ], + "preset": "preset.ts", + "devDependencies": { + "apply": "^0.2.13" + } +} diff --git a/preset.ts b/preset.ts new file mode 100644 index 0000000..cdd7c9b --- /dev/null +++ b/preset.ts @@ -0,0 +1,201 @@ +import { Preset, color } from 'apply'; + +type Dependencies = { + [key: string]: { + version: string; + type?: 'DEV' | 'PEER'; + reliesOn?: string; + } +} + +type Configuration = { + [key: string]: { + message: string; + default: any; + question?: true; + } +} + +/** + * Svelte adder utility class. + * + * Used to simplify interaction with Preset and approach + * file-structure modification in a configuration-based way. + */ +abstract class Adder { + + /** + * The adder's name, which is displayed as the name + * of the Preset. Specified on the implementation level. + * + * @protected + */ + protected abstract readonly ADDER_NAME: string; + + /** + * A dictionary of configuration options. Each option + * will either be determined interactively; when the + * `--interaction` flag is present, or through CLI flags. + * + * @protected + */ + protected abstract readonly CONFIGURATION: Configuration; + + /** + * A dictionary of required dependencies. Each dependency + * has an associated version, and the type can be either + * explicitly set as 'DEV' or 'PEER', or implicitly + * inferred as a core dependency when left `undefined`. + * + * Each dependency can also specify whether or not they + * should be installed based on the presence of a + * configuration option, defined in `CONFIGURATION`. + * + * @protected + */ + protected abstract readonly REQUIRED_DEPENDENCIES: Dependencies; + + /** + * Runs an adder. Initialises all configuration and + * dependencies, followed by running the implementation + * specific functionality. + */ + run(): void { + this.initialiseAdder(); + } + + /** + * Safely extracts a file, ensuring the user acknowledges + * that their previously defined data will be overwritten. + * + * @param title The title of the specific action being + * performed. + * @param filename The filename to move from the templates + * folder. + * @protected + */ + protected safeExtract(title: string, filename: string) { + return Preset.extract(filename).whenConflict('ask').withTitle(title); + } + + protected getConfiguration(key): T { + return Preset.isInteractive() ? Preset.prompts[key] : Preset.options[key]; + } + + private initialiseAdder(): void { + Preset.setName(this.ADDER_NAME); + this.setupConfiguration(); + this.setupDependencies(); + } + + private setupConfiguration(): void { + + Object.keys(this.CONFIGURATION).forEach(configurationKey => { + + const configuration = this.CONFIGURATION[configurationKey]; + + this.configure( + configurationKey, + configuration.message, + configuration.default, + configuration.question || false + ); + }); + } + + private setupDependencies(): void { + + Preset.group(preset => { + + Object.keys(this.REQUIRED_DEPENDENCIES).forEach(dependencyName => { + + const dependencyConfig = this.REQUIRED_DEPENDENCIES[dependencyName]; + + let action; + + switch (dependencyConfig.type) { + case 'DEV': + action = preset.editNodePackages().addDev(dependencyName, dependencyConfig.version); + break; + case 'PEER': + action = preset.editNodePackages().addPeer(dependencyName, dependencyConfig.version); + break; + case undefined: + action = preset.editNodePackages().add(dependencyName, dependencyConfig.version); + break; + } + + action.if(() => dependencyConfig.reliesOn + ? this.getConfiguration(dependencyConfig.reliesOn) + : true + ); + }); + + }).withTitle('Adding required dependencies'); + } + + private configure(key: string, msg: string, init: any, confirm: boolean): void { + + Preset + .group(preset => { + preset.confirm(key, msg, init).if(() => confirm); + preset.input(key, msg, init).if(() => !confirm); + }) + .withoutTitle() + .ifInteractive(); + + Preset + .group(preset => preset.option(key, init)) + .withoutTitle() + .if(() => !Preset.isInteractive()); + } +} + +class SvelteJestAdder extends Adder { + + protected readonly ADDER_NAME = '@rossyman/svelte-add-jest'; + + protected readonly CONFIGURATION: Configuration = { + 'jest-dom': {message: 'Enable Jest DOM support?', default: true, question: true}, + 'examples': {message: 'Generate example test file?', default: true, question: true} + }; + + protected readonly REQUIRED_DEPENDENCIES: Dependencies = { + '@babel/core': {version: '^7.13.0', type: 'DEV'}, + '@babel/preset-env': {version: '^7.13.0', type: 'DEV'}, + 'jest': {version: '^26.6.0', type: 'DEV'}, + 'babel-jest': {version: '^26.6.0', type: 'DEV'}, + 'svelte-jester': {version: '^1.3.0', type: 'DEV'}, + '@testing-library/svelte': {version: '^3.0.0', type: 'DEV'}, + '@testing-library/jest-dom': {version: '^5.11.0', type: 'DEV', reliesOn: 'jest-dom'}, + }; + + run(): void { + + super.run(); + + this.safeExtract('Initializing Jest config', 'jest.config.json'); + this.safeExtract('Initializing Babel config', '.babelrc'); + + Preset + .editJson('jest.config.json') + .merge({setupFilesAfterEnv: ['@testing-library/jest-dom/extend-expect']}) + .withTitle('Enabling Jest DOM Support') + .if(() => this.getConfiguration('jest-dom')); + + this.safeExtract('Initializing example test file', 'index.spec.js') + .to('src/routes/') + .if(() => this.getConfiguration('examples') && this.getConfiguration('jest-dom')); + + Preset + .editJson('package.json') + .merge({scripts: {'test': 'jest src --config jest.config.json', 'test:watch': 'npm run test -- --watch'}}) + .withTitle('Adding test scripts to package.json'); + + Preset + .instruct(`Run ${color.magenta("npm install")}, ${color.magenta("pnpm install")}, or ${color.magenta("yarn")} to install dependencies`) + .withHeading("What's next?"); + } +} + +new SvelteJestAdder().run(); diff --git a/templates/.babelrc b/templates/.babelrc new file mode 100644 index 0000000..522c7e1 --- /dev/null +++ b/templates/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "targets": { + "node": "current" + } + } + ] + ] +} \ No newline at end of file diff --git a/templates/index.spec.js b/templates/index.spec.js new file mode 100644 index 0000000..3315722 --- /dev/null +++ b/templates/index.spec.js @@ -0,0 +1,8 @@ +import "@testing-library/jest-dom/extend-expect"; +import { render } from "@testing-library/svelte"; +import index from "./index.svelte"; + +test("shows proper heading when rendered", () => { + const { getByText } = render(index); + expect(getByText("Hello world!")).toBeInTheDocument(); +}); diff --git a/templates/jest.config.json b/templates/jest.config.json new file mode 100644 index 0000000..4907484 --- /dev/null +++ b/templates/jest.config.json @@ -0,0 +1,11 @@ +{ + "transform": { + "^.+\\.js$": "babel-jest", + "^.+\\.svelte$": "svelte-jester" + }, + "moduleNameMapper": { + "^\\$lib(.*)$": "/src/lib$1", + "^\\$app(.*)$": [".svelte/dev/runtime/app/*", ".svelte/build/runtime/app/*"] + }, + "moduleFileExtensions": ["js", "svelte"] +}