From f5146072a1dbc0ddf3fc26774c119ed3a8dcd5da Mon Sep 17 00:00:00 2001 From: Iacopo Ciao Date: Tue, 4 Jun 2024 15:35:50 +0200 Subject: [PATCH] chore: initial commit --- .eslintignore | 5 ++ .eslintrc.json | 50 +++++++++++ .github/workflows/release.yml | 36 ++++++++ .gitignore | 2 + .npmignore | 13 +++ .prettierrc.json | 6 ++ .releaserc.js | 22 +++++ README.md | 106 +++++++++++++++++++++++ lib/src/define-config.ts | 25 ++++++ lib/src/index.ts | 3 + lib/src/presets/component-preset.ts | 26 ++++++ lib/src/presets/e2e-preset.ts | 26 ++++++ lib/src/presets/index.ts | 2 + lib/src/utilities/index.ts | 1 + lib/src/utilities/parse-env-variables.ts | 16 ++++ package.json | 77 ++++++++++++++++ tsconfig.json | 15 ++++ tsup.config.ts | 14 +++ 18 files changed, 445 insertions(+) create mode 100644 .eslintignore create mode 100644 .eslintrc.json create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 .prettierrc.json create mode 100644 .releaserc.js create mode 100644 README.md create mode 100644 lib/src/define-config.ts create mode 100644 lib/src/index.ts create mode 100644 lib/src/presets/component-preset.ts create mode 100644 lib/src/presets/e2e-preset.ts create mode 100644 lib/src/presets/index.ts create mode 100644 lib/src/utilities/index.ts create mode 100644 lib/src/utilities/parse-env-variables.ts create mode 100644 package.json create mode 100644 tsconfig.json create mode 100644 tsup.config.ts diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..fb63ead --- /dev/null +++ b/.eslintignore @@ -0,0 +1,5 @@ +dist +package.json +./node_modules/ +.vscode +.idea diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..03a8d20 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,50 @@ +{ + "root": true, + "parserOptions": { + "ecmaVersion": "latest" + }, + "env": { + "es6": true + }, + "plugins": ["simple-import-sort"], + "overrides": [ + { + "parserOptions": { + "project": "./tsconfig.json" + }, + "files": [ + "lib/src/**/*.ts" + ], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended" + ], + "rules": { + "simple-import-sort/imports": "error", + "simple-import-sort/exports": "error", + "@typescript-eslint/ban-ts-comment": 0, + "max-lines-per-function": [ + 1, + { + "max": 40 + } + ], + "max-lines": [ + 1, + { + "max": 150 + } + ] + } + }, + { + "parserOptions": { + "project": "./tsconfig.json" + }, + "files": [ + "lib/src/**/*.ts" + ] + } + ] +} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..21dfaee --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,36 @@ +name: Release + +on: + push: + branches: + - main + +jobs: + release: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: '20' + + - name: Setup Corepack + run: | + corepack enable + corepack prepare npm@10.8.1 --activate + + - name: Install dependencies + run: npm install + + - name: Build project + run: npm run build + + - name: Run Semantic Release + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + run: npm run release diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..76add87 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +dist \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..c6d24c7 --- /dev/null +++ b/.npmignore @@ -0,0 +1,13 @@ +.idea/ +.github +.gitignore +.prettierrc.json +.DS_Store +CONTRIBUTE.md + +# configuration +renovate.json +tsconfig.json +/.vscode +.eslintrc.json +.eslintignore \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..0d2c227 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "singleQuote": true, + "trailingComma": "all", + "endOfLine": "auto", + "printWidth": 100 +} diff --git a/.releaserc.js b/.releaserc.js new file mode 100644 index 0000000..232b3b3 --- /dev/null +++ b/.releaserc.js @@ -0,0 +1,22 @@ +/** + * @type {import('semantic-release').GlobalConfig} + */ +const releaseConfig = { + branches: ["main"], + "plugins": [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + "@semantic-release/changelog", + "@semantic-release/npm", + "@semantic-release/github", + [ + "@semantic-release/git", + { + "assets": ["package.json", "CHANGELOG.md"], + "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" + } + ] + ] +}; + +module.exports = releaseConfig; \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..58db601 --- /dev/null +++ b/README.md @@ -0,0 +1,106 @@ +# @devmy/cypress-dotenv + +`@devmy/cypress-dotenv` is a TypeScript library designed to integrate `dotenv` configuration with `@dotenv-run/core` into your Cypress setup, simplifying the management of environment variables for both component and end-to-end (E2E) tests. + +- ✅ Load environment variables from .env files +- ✅ Load environment variables from .env.vault files +- ✅ Expand environment variables API_URL=$API_BASE/users +- ✅ Define environment variables for a specific environment (e.g. .env.production) +- ✅ Load priorities of .env.* files (e.g. .env.production > .env) +- ✅ Hierarchical cascading configuration in monorepo projects (Nx, Turbo, etc.) apps/next-app/.env > apps/.env > .env + +## Installation + +Install the package via npm: + +```bash +npm install @devmy/cypress-dotenv +``` + +or via yarn: + +```bash +yarn add @devmy/cypress-dotenv +``` + +or via pnpm: + +```bash +pnpm add @devmy/cypress-dotenv +``` + +## Usage + +### Dotenv Configuration +The `dotenv` configuration options in this library use `DotenvRunOptions` of [dotenv-run/core](https://www.npmjs.com/package/@dotenv-run/core). + +### `defineConfigWithDotenv` + +The `defineConfigWithDotenv` function invokes Cypress's `defineConfig`, loading environment variables from `dotenv`. + +#### Example: + +```typescript +import { defineConfigWithDotenv } from '@devmy/cypress-dotenv'; + +export default defineConfigWithDotenv({ + dotenv: { + prefix: 'FRONTEND_E2E_', + root: '../../', + }, + component: { + specPattern: 'src/**/*.cy.ts', + }, + e2e: { + baseUrl: 'http://localhost:3000', + }, +}); +``` + +### `dotenvComponentPreset` + +The `dotenvComponentPreset` function loads `dotenv` for component tests in Cypress. + +#### Example: + +```typescript +import { dotenvComponentPreset } from '@devmy/cypress-dotenv'; + +export default dotenvComponentPreset({ + dotenv: { + prefix: 'FRONTEND_E2E_', + root: '../../', + }, + devServer: { + framework: 'react', + bundler: 'webpack', + }, +}); +``` + +### `dotenvE2EPreset` + +The `dotenvE2EPreset` function loads `dotenv` for E2E tests in Cypress. + +#### Example: + +```typescript +import { dotenvE2EPreset } from '@devmy/cypress-dotenv'; + +export default dotenvE2EPreset({ + dotenv: { + prefix: 'FRONTEND_E2E_', + root: '../../', + }, + baseUrl: 'http://localhost:3000', +}); +``` + + +## License + +This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. + +## Contributing + +Contributions are welcome! Please open an issue or submit a pull request for any changes. \ No newline at end of file diff --git a/lib/src/define-config.ts b/lib/src/define-config.ts new file mode 100644 index 0000000..ff3febd --- /dev/null +++ b/lib/src/define-config.ts @@ -0,0 +1,25 @@ +import { DotenvRunOptions } from '@dotenv-run/core'; +import { defineConfig } from 'cypress'; + +import { parseEnvVariables } from './utilities'; + +export type CyDefineConfig = Parameters[0]; + +export type DotenvConfigOptions = CyDefineConfig & { + dotenv: DotenvRunOptions; +}; + +export const defineConfigWithDotenv = ( + dotenvConfigOptions: DotenvConfigOptions, +): CyDefineConfig => { + const { dotenv, ...config } = dotenvConfigOptions; + const oldSetupNodeEvents = config.setupNodeEvents; + + config.setupNodeEvents = (onPluginEvent, pluginConfigOptions) => { + const nextPluginConfigOptions = parseEnvVariables(dotenv, pluginConfigOptions); + + return oldSetupNodeEvents?.(onPluginEvent, nextPluginConfigOptions); + }; + + return defineConfig(config); +}; diff --git a/lib/src/index.ts b/lib/src/index.ts new file mode 100644 index 0000000..7b9da67 --- /dev/null +++ b/lib/src/index.ts @@ -0,0 +1,3 @@ +export * from './define-config'; +export * from './presets'; +export * from './utilities'; diff --git a/lib/src/presets/component-preset.ts b/lib/src/presets/component-preset.ts new file mode 100644 index 0000000..1fd9a6a --- /dev/null +++ b/lib/src/presets/component-preset.ts @@ -0,0 +1,26 @@ +import type { DotenvRunOptions } from '@dotenv-run/core'; +import { defineConfig } from 'cypress'; + +import { parseEnvVariables } from '../utilities'; + +export type CyDefineComponentConfig = Parameters[0]['component']; + +export type DotenvComponentPresetConfigOptions = CyDefineComponentConfig & { + dotenv?: DotenvRunOptions; +}; + +export const dotenvComponentPreset = ( + dotenvComponentPresetConfigOptions: DotenvComponentPresetConfigOptions, +): CyDefineComponentConfig => { + const { dotenv, ...config } = dotenvComponentPresetConfigOptions; + + const oldSetupNodeEvents = config.setupNodeEvents; + + config.setupNodeEvents = (onPluginEvent, pluginConfigOptions) => { + const nextPluginConfigOptions = parseEnvVariables(dotenv, pluginConfigOptions); + + return oldSetupNodeEvents?.(onPluginEvent, nextPluginConfigOptions); + }; + + return config; +}; diff --git a/lib/src/presets/e2e-preset.ts b/lib/src/presets/e2e-preset.ts new file mode 100644 index 0000000..55607ae --- /dev/null +++ b/lib/src/presets/e2e-preset.ts @@ -0,0 +1,26 @@ +import type { DotenvRunOptions } from '@dotenv-run/core'; +import { defineConfig } from 'cypress'; + +import { parseEnvVariables } from '../utilities'; + +export type CyDefineE2EConfig = Parameters[0]['e2e']; + +export type DotenvE2EPresetConfigOptions = CyDefineE2EConfig & { + dotenv?: DotenvRunOptions; +}; + +export const dotenvE2EPreset = ( + dotenvE2EPresetConfigOptions: DotenvE2EPresetConfigOptions, +): CyDefineE2EConfig => { + const { dotenv, ...config } = dotenvE2EPresetConfigOptions; + + const oldSetupNodeEvents = config.setupNodeEvents; + + config.setupNodeEvents = (onPluginEvent, pluginConfigOptions) => { + const nextPluginConfigOptions = parseEnvVariables(dotenv, pluginConfigOptions); + + return oldSetupNodeEvents?.(onPluginEvent, nextPluginConfigOptions); + }; + + return config; +}; diff --git a/lib/src/presets/index.ts b/lib/src/presets/index.ts new file mode 100644 index 0000000..f68f7e9 --- /dev/null +++ b/lib/src/presets/index.ts @@ -0,0 +1,2 @@ +export * from './component-preset'; +export * from './e2e-preset'; diff --git a/lib/src/utilities/index.ts b/lib/src/utilities/index.ts new file mode 100644 index 0000000..22e582e --- /dev/null +++ b/lib/src/utilities/index.ts @@ -0,0 +1 @@ +export * from './parse-env-variables'; diff --git a/lib/src/utilities/parse-env-variables.ts b/lib/src/utilities/parse-env-variables.ts new file mode 100644 index 0000000..44edbb4 --- /dev/null +++ b/lib/src/utilities/parse-env-variables.ts @@ -0,0 +1,16 @@ +import { DotenvRunOptions, env } from '@dotenv-run/core'; +import { cloneDeep, entries } from 'lodash'; + +export const parseEnvVariables = ( + options: DotenvRunOptions | undefined, + config: Cypress.PluginConfigOptions, +) => { + const dotenvRun = env(options); + + const enhancedConfig = cloneDeep(config); + enhancedConfig.env = enhancedConfig.env ?? {}; + + entries(dotenvRun.raw).forEach(([key, value]) => (enhancedConfig.env[key] = value)); + + return enhancedConfig; +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..a3ee9ec --- /dev/null +++ b/package.json @@ -0,0 +1,77 @@ +{ + "name": "@devmy/cypress-dotenv", + "version": "0.0.0", + "description": "integrate `dotenv` configuration with `@dotenv-run/core` into your Cypress setup, simplifying the management of environment variables for both component and end-to-end (E2E) tests.", + "type": "commonjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "exports": { + "import": "./dist/index.mjs", + "require": "./dist/index.js", + "default": "./dist/index.js", + "types": "./dist/index.d.ts" + }, + "scripts": { + "build": "tsup", + "lint": "eslint 'lib/src/**/*.ts'", + "lint:fix": "eslint 'lib/src/**/*.ts' --fix", + "release": "semantic-release" + }, + "keywords": [ + "cypress", + "dotenv", + "environment variables", + "testing", + "typescript", + "component testing", + "end-to-end testing", + "e2e", + "cypress config", + "env", + "dotenv-run", + ".env.vault", + "vault", + "dotenv_key" + ], + "license": "MIT", + "author": ".devmy", + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/acadevmy/cypress-dotenv.git" + }, + "bugs": { + "url": "https://github.com/acadevmy/cypress-dotenv/issues" + }, + "dependencies": { + "@dotenv-run/core": "^1.3.5", + "cypress": "^13.10.0", + "lodash": "^4.17.21" + }, + "devDependencies": { + "@semantic-release/changelog": "^6.0.3", + "@semantic-release/commit-analyzer": "^13.0.0", + "@semantic-release/git": "^10.0.1", + "@semantic-release/github": "github:semantic-release/git", + "@semantic-release/npm": "^12.0.1", + "@semantic-release/release-notes-generator": "^14.0.0", + "@types/lodash": "^4.17.4", + "@types/node": "^20.14.0", + "@typescript-eslint/eslint-plugin": "^7.11.0", + "eslint": "8.57.0", + "eslint-config-prettier": "^8.10.0", + "eslint-plugin-cypress": "2.15.1", + "eslint-plugin-prettier": "^5.1.3", + "eslint-plugin-simple-import-sort": "^12.1.0", + "prettier": "^3.2.5", + "semantic-release": "^24.0.0", + "tsup": "^8.1.0", + "typescript": "^5.3.3" + }, + "packageManager": "npm@10.8.1" +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..3c1a843 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "declaration": true, + "module": "commonjs", + "target": "ES2021", + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "useUnknownInCatchVariables": false, + "esModuleInterop": true, + "outDir": "./dist" + }, + "include": ["lib/src"], + "exclude": ["node_modules"] +} \ No newline at end of file diff --git a/tsup.config.ts b/tsup.config.ts new file mode 100644 index 0000000..d5bbe9d --- /dev/null +++ b/tsup.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["lib/src/index.ts"], + format: ["cjs", "esm"], + dts: true, + splitting: true, + treeshake: true, + sourcemap: true, + minify: true, + clean: true, + cjsInterop: true, + tsconfig: './tsconfig.json' +}); \ No newline at end of file