├── .circleci
└── config.yml
├── .editorconfig
├── .gitattributes
├── .gitignore
├── .husky
├── _
│ └── husky.sh
├── post-commit
└── pre-commit
├── .npmrc
├── .nvmrc
├── .prettierignore
├── CHANGELOG.md
├── eslint.config.mjs
├── jest-dynamodb-config.js
├── jest-preset.js
├── license
├── package.json
├── readme.md
├── renovate.json
├── src
├── environment.ts
├── index.ts
├── setup.ts
├── teardown.ts
├── types.ts
└── utils
│ ├── delete-tables.ts
│ ├── get-config.ts
│ ├── get-relevant-tables.ts
│ └── wait-for-localhost.ts
├── tests
├── jest-preset-concurrent.test.ts
└── jest-preset.test.ts
└── tsconfig.json
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 |
3 | parameters:
4 | node_docker_image:
5 | type: string
6 | default: cimg/node:22.14.0-browsers
7 |
8 | jobs:
9 | test_without_db:
10 | working_directory: ~/repo
11 | docker:
12 | - image: << parameters.node_docker_image >>
13 | steps:
14 | - checkout
15 | - run: yarn
16 | - run: yarn build
17 | - run: yarn test
18 |
19 | test_with_db:
20 | working_directory: ~/repo
21 | docker:
22 | - image: << parameters.node_docker_image >>
23 | - image: circleci/dynamodb
24 | steps:
25 | - checkout
26 | - run: yarn
27 | - run: yarn build
28 | - run: yarn test
29 |
30 | workflows:
31 | version: 2
32 | build_and_test:
33 | jobs:
34 | - test_with_db
35 | - test_without_db
36 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | charset = utf-8
6 | trim_trailing_whitespace = true
7 | insert_final_newline = true
8 | indent_style = space
9 | indent_size = 2
10 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | *.js text eol=lf
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | coverage/
3 | node_modules/
4 | lib/
5 | temp
6 | yarn.lock
7 | *.log
8 | .DS_Store
9 | dist/
10 | !.husky/_/husky.sh
11 |
--------------------------------------------------------------------------------
/.husky/_/husky.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | if [ -z "$husky_skip_init" ]; then
3 | debug () {
4 | if [ "$HUSKY_DEBUG" = "1" ]; then
5 | echo "husky (debug) - $1"
6 | fi
7 | }
8 |
9 | readonly hook_name="$(basename "$0")"
10 | debug "starting $hook_name..."
11 |
12 | if [ "$HUSKY" = "0" ]; then
13 | debug "HUSKY env variable is set to 0, skipping hook"
14 | exit 0
15 | fi
16 |
17 | if [ -f ~/.huskyrc ]; then
18 | debug "sourcing ~/.huskyrc"
19 | . ~/.huskyrc
20 | fi
21 |
22 | export readonly husky_skip_init=1
23 | sh -e "$0" "$@"
24 | exitCode="$?"
25 |
26 | if [ $exitCode != 0 ]; then
27 | echo "husky - $hook_name hook exited with code $exitCode (error)"
28 | fi
29 |
30 | exit $exitCode
31 | fi
32 |
--------------------------------------------------------------------------------
/.husky/post-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 | git update-index --again
4 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 | yarn lint-staged
4 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v22.13.0
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | package.json
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Breaking Changes
2 |
3 | ## 4.0.0
4 |
5 | - Switched `node` version `18`->`22`
6 |
--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import rules from '@shelf/eslint-config/typescript.js';
2 |
3 | export default [
4 | ...rules,
5 | {files: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx', '**/*.json']},
6 | {
7 | ignores: [
8 | '.idea/',
9 | 'coverage/',
10 | 'draft.js',
11 | 'lib/',
12 | 'dist/',
13 | 'node_modules/',
14 | 'packages/**/tsconfig.types.json',
15 | 'packages/**/node_modules/**',
16 | 'packages/**/lib/**',
17 | 'renovate.json',
18 | ],
19 | },
20 | ];
21 |
--------------------------------------------------------------------------------
/jest-dynamodb-config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @type {import('./lib/types').Config}
3 | */
4 | const config = {
5 | tables: [
6 | {
7 | TableName: `files`,
8 | KeySchema: [{AttributeName: 'id', KeyType: 'HASH'}],
9 | AttributeDefinitions: [{AttributeName: 'id', AttributeType: 'S'}],
10 | ProvisionedThroughput: {ReadCapacityUnits: 1, WriteCapacityUnits: 1},
11 | },
12 | {
13 | TableName: `users`,
14 | KeySchema: [{AttributeName: 'id', KeyType: 'HASH'}],
15 | AttributeDefinitions: [{AttributeName: 'id', AttributeType: 'S'}],
16 | ProvisionedThroughput: {ReadCapacityUnits: 1, WriteCapacityUnits: 1},
17 | },
18 | ],
19 | port: 8000,
20 | options: ['-sharedDb'],
21 | };
22 |
23 | module.exports = config;
24 |
--------------------------------------------------------------------------------
/jest-preset.js:
--------------------------------------------------------------------------------
1 | const preset = require('./lib').default;
2 |
3 | module.exports = preset;
4 |
--------------------------------------------------------------------------------
/license:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Gemshelf Inc. (shelf.io)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@shelf/jest-dynamodb",
3 | "version": "4.0.0",
4 | "description": "Run your tests using Jest & DynamoDB local",
5 | "keywords": [
6 | "dynamodb",
7 | "dynamodb local",
8 | "jest",
9 | "jest environment",
10 | "jest preset"
11 | ],
12 | "repository": "shelfio/jest-dynamodb",
13 | "license": "MIT",
14 | "author": {
15 | "name": "Vlad Holubiev",
16 | "email": "vlad@shelf.io",
17 | "url": "shelf.io"
18 | },
19 | "files": [
20 | "jest-preset.js",
21 | "lib/"
22 | ],
23 | "scripts": {
24 | "build": "rm -rf lib/ && yarn build:types && babel src --out-dir lib --ignore '**/*.test.ts' --extensions '.ts'",
25 | "build:types": "tsc --emitDeclarationOnly --declaration --isolatedModules false --declarationDir lib",
26 | "coverage": "jest --coverage",
27 | "lint": "yarn lint:ci --fix",
28 | "lint:ci": "eslint . --quiet",
29 | "prepack": "yarn build",
30 | "test": "export ENVIRONMENT=local && jest tests",
31 | "type-check": "tsc --noEmit",
32 | "type-check:watch": "npm run type-check -- --watch"
33 | },
34 | "lint-staged": {
35 | "*.{html,md,yml}": [
36 | "prettier --write"
37 | ],
38 | "*.{ts,js,json}": [
39 | "eslint --fix"
40 | ]
41 | },
42 | "babel": {
43 | "extends": "@shelf/babel-config/backend"
44 | },
45 | "prettier": "@shelf/prettier-config",
46 | "jest": {
47 | "preset": "./jest-preset.js"
48 | },
49 | "dependencies": {
50 | "@aws-sdk/client-dynamodb": "3.624.0",
51 | "@aws-sdk/lib-dynamodb": "3.624.0",
52 | "@aws-sdk/util-dynamodb": "3.624.0",
53 | "cwd": "0.10.0",
54 | "debug": "4.3.4",
55 | "dynamodb-local": "0.0.34"
56 | },
57 | "devDependencies": {
58 | "@babel/cli": "7.27.0",
59 | "@babel/core": "7.26.10",
60 | "@shelf/babel-config": "1.2.0",
61 | "@shelf/eslint-config": "4.2.1",
62 | "@shelf/prettier-config": "1.0.0",
63 | "@shelf/tsconfig": "0.1.0",
64 | "@types/aws-sdk": "2.7.4",
65 | "@types/cwd": "^0.10.2",
66 | "@types/jest": "29.5.14",
67 | "@types/node": "22",
68 | "eslint": "9.25.1",
69 | "husky": "8.0.3",
70 | "jest": "29.7.0",
71 | "lint-staged": "13.3.0",
72 | "prettier": "3.5.3",
73 | "typescript": "5.8.3"
74 | },
75 | "engines": {
76 | "node": ">=22"
77 | },
78 | "publishConfig": {
79 | "access": "public"
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # jest-dynamodb [](https://circleci.com/gh/shelfio/jest-dynamodb/tree/master)  [](https://www.npmjs.com/package/@shelf/jest-dynamodb)
2 |
3 | > Jest preset to run DynamoDB Local
4 |
5 | ## Usage
6 |
7 | ### 0. Install
8 |
9 | ```
10 | $ yarn add @shelf/jest-dynamodb --dev
11 | ```
12 |
13 | Make sure `java` runtime available for running [DynamoDBLocal.jar](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html)
14 |
15 | ### 1. Create `jest.config.js`
16 |
17 | ```js
18 | module.exports = {
19 | preset: '@shelf/jest-dynamodb',
20 | };
21 | ```
22 |
23 | ### 2. Create `jest-dynamodb-config.js`
24 |
25 | #### 2.1 Properties
26 |
27 | ##### tables
28 |
29 | - Type: `object[]`
30 | - Required: `true`
31 |
32 | Array of createTable params.
33 |
34 | - [Create Table API](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#createTable-property).
35 |
36 | ##### port
37 |
38 | - Type: `number`
39 | - Required: `false`
40 |
41 | Port number. The default port number is `8000`.
42 |
43 | ##### hostname
44 |
45 | - Type: `string`
46 | - Required: `false`
47 |
48 | Hostname. The default hostname is `localhost`.
49 |
50 | ##### options
51 |
52 | - Type: `string[]`
53 | - Required: `false`
54 |
55 | Additional arguments for dynamodb-local. The default value is `['-sharedDb']`.
56 |
57 | - [dynamodb-local](https://github.com/rynop/dynamodb-local)
58 | - [DynamoDB Local Usage Notes](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.UsageNotes.html)
59 |
60 | ##### clientConfig
61 |
62 | - Type: `object`
63 | - Required: `false`
64 |
65 | Constructor params of DynamoDB client.
66 |
67 | - [Constructor Property](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#constructor-property)
68 |
69 | ##### installerConfig
70 |
71 | - Type: `{installPath?: string, downloadUrl?: string}`
72 | - Required: `false`
73 |
74 | - `installPath` defines the location where dynamodb-local is installed or will be installed.
75 | - `downloadUrl` defines the url of dynamodb-local package.
76 |
77 | The default value is defined at https://github.com/rynop/dynamodb-local/blob/2e6c1cb2edde4de0dc51a71c193c510b939d4352/index.js#L16-L19
78 |
79 | #### 2.2 Examples
80 |
81 | You can set up tables as an object:
82 | > Whole list of config properties can be found [here](https://github.com/shelfio/jest-dynamodb/blob/6c64dbd4ee5a68230469ea14cbfb814470521197/src/types.ts#L80-L87)
83 | ```js
84 | /**
85 | * @type {import('@shelf/jest-dynamodb/lib').Config}')}
86 | */
87 | const config = {
88 | tables: [
89 | {
90 | TableName: `files`,
91 | KeySchema: [{AttributeName: 'id', KeyType: 'HASH'}],
92 | AttributeDefinitions: [{AttributeName: 'id', AttributeType: 'S'}],
93 | ProvisionedThroughput: {ReadCapacityUnits: 1, WriteCapacityUnits: 1},
94 | },
95 | // etc
96 | ],
97 | port: 8000,
98 | };
99 | module.exports = config;
100 | ```
101 |
102 | Or as an async function (particularly useful when resolving DynamoDB setup dynamically from `serverless.yml`):
103 |
104 | ```js
105 | module.exports = async () => {
106 | const serverless = new (require('serverless'))();
107 | // If using monorepo where DynamoDB serverless.yml is in another directory
108 | // const serverless = new (require('serverless'))({ servicePath: '../../../core/data' });
109 |
110 | await serverless.init();
111 | const service = await serverless.variables.populateService();
112 | const resources = service.resources.filter(r => Object.keys(r).includes('Resources'))[0];
113 |
114 | const tables = Object.keys(resources)
115 | .map(name => resources[name])
116 | .filter(r => r.Type === 'AWS::DynamoDB::Table')
117 | .map(r => r.Properties);
118 |
119 | return {
120 | tables,
121 | port: 8000,
122 | };
123 | };
124 | ```
125 |
126 | Or read table definitions from a CloudFormation template (example handles a !Sub on TableName, i.e. TableName: !Sub "\${env}-users" ):
127 |
128 | ```js
129 | const yaml = require('js-yaml');
130 | const fs = require('fs');
131 | const {CLOUDFORMATION_SCHEMA} = require('cloudformation-js-yaml-schema');
132 |
133 | module.exports = async () => {
134 | const cf = yaml.load(fs.readFileSync('../cf-templates/example-stack.yaml', 'utf8'), {
135 | schema: CLOUDFORMATION_SCHEMA,
136 | });
137 | var tables = [];
138 | Object.keys(cf.Resources).forEach(item => {
139 | tables.push(cf.Resources[item]);
140 | });
141 |
142 | tables = tables
143 | .filter(r => r.Type === 'AWS::DynamoDB::Table')
144 | .map(r => {
145 | let table = r.Properties;
146 | if (typeof r.TableName === 'object') {
147 | table.TableName = table.TableName.data.replace('${env}', 'test');
148 | }
149 | delete table.TimeToLiveSpecification; //errors on dynamo-local
150 | return table;
151 | });
152 |
153 | return {
154 | tables,
155 | port: 8000,
156 | };
157 | };
158 | ```
159 |
160 | ### 3.1 Configure DynamoDB client (from aws-sdk v2)
161 |
162 | ```js
163 | const {DocumentClient} = require('aws-sdk/clients/dynamodb');
164 |
165 | const isTest = process.env.JEST_WORKER_ID;
166 | const config = {
167 | convertEmptyValues: true,
168 | ...(isTest && {
169 | endpoint: 'localhost:8000',
170 | sslEnabled: false,
171 | region: 'local-env',
172 | credentials: {
173 | accessKeyId: 'fakeMyKeyId',
174 | secretAccessKey: 'fakeSecretAccessKey',
175 | },
176 | }),
177 | };
178 |
179 | const ddb = new DocumentClient(config);
180 | ```
181 |
182 | ### 3.2 Configure DynamoDB client (from aws-sdk v3)
183 |
184 | ```js
185 | const {DynamoDB} = require('@aws-sdk/client-dynamodb');
186 | const {DynamoDBDocument} = require('@aws-sdk/lib-dynamodb');
187 |
188 | const isTest = process.env.JEST_WORKER_ID;
189 |
190 | const ddb = DynamoDBDocument.from(
191 | new DynamoDB({
192 | ...(isTest && {
193 | endpoint: 'http://localhost:8000',
194 | region: 'local-env',
195 | credentials: {
196 | accessKeyId: 'fakeMyKeyId',
197 | secretAccessKey: 'fakeSecretAccessKey',
198 | },
199 | }),
200 | }),
201 | {
202 | marshallOptions: {
203 | convertEmptyValues: true,
204 | },
205 | }
206 | );
207 | ```
208 |
209 | ### 4. PROFIT! Write tests
210 |
211 | ```js
212 | it('should insert item into table', async () => {
213 | await ddb.put({TableName: 'files', Item: {id: '1', hello: 'world'}}).promise();
214 |
215 | const {Item} = await ddb.get({TableName: 'files', Key: {id: '1'}}).promise();
216 |
217 | expect(Item).toEqual({
218 | id: '1',
219 | hello: 'world',
220 | });
221 | });
222 | ```
223 |
224 | ## Monorepo Support
225 |
226 | By default the `jest-dynamodb-config.js` is read from `cwd` directory, but this might not be suitable for monorepos with nested [jest projects](https://jestjs.io/docs/configuration#projects-arraystring--projectconfig) with nested `jest.config.*` files nested in subdirectories.
227 |
228 | If your `jest-dynamodb-config.js` file is not located at `{cwd}/jest-dynamodb-config.js` or you are using nested `jest projects`, you can define the environment variable `JEST_DYNAMODB_CONFIG` with the absolute path of the respective `jest-dynamodb-config.js` file.
229 |
230 | ### Example Using `JEST_DYNAMODB_CONFIG` in nested project
231 |
232 | ```
233 | // src/nested/project/jest.config.js
234 | const path = require('path');
235 |
236 | // Define path of project level config - extension not required as file will be imported via `require(process.env.JEST_DYNAMODB_CONFIG)`
237 | process.env.JEST_DYNAMODB_CONFIG = path.resolve(__dirname, './jest-dynamodb-config');
238 |
239 | module.exports = {
240 | preset: '@shelf/jest-dynamodb'
241 | displayName: 'nested-project',
242 | };
243 | ```
244 |
245 | ## Troubleshooting
246 |
247 |
248 | UnknownError: Not Found
249 |
250 | Perhaps something is using your port specified in `jest-dynamodb-config.js`.
251 |
252 |
253 |
254 |
255 | com.almworks.sqlite4java.Internal log WARNING: [sqlite] cannot open DB[1]:
256 |
257 | See https://www.josephso.dev/using-jest-dynamodb-in-apple-silicon-platform-workaround/#community-build
258 |
259 |
260 |
261 | ## Alternatives
262 |
263 | - [jest-dynalite](https://github.com/freshollie/jest-dynalite) - a much lighter version which spins up an instance for each runner & doesn't depend on Java
264 |
265 | ## Read
266 |
267 | - [dynamodb-local](https://github.com/rynop/dynamodb-local)
268 | - [DynamoDB Local Usage Notes](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.UsageNotes.html)
269 | - [Create Table API](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB.html#createTable-property)
270 |
271 | ## Used by
272 |
273 | - [dynamodb-parallel-scan](https://github.com/shelfio/dynamodb-parallel-scan)
274 | - [@nasa-gcn/dynamodb-autoincrement](https://github.com/nasa-gcn/dynamodb-autoincrement)
275 |
276 | ## See Also
277 |
278 | - [jest-mongodb](https://github.com/shelfio/jest-mongodb)
279 |
280 | ## Publish
281 |
282 | ```sh
283 | $ git checkout master
284 | $ yarn version
285 | $ yarn publish
286 | $ git push origin master --tags
287 | ```
288 |
289 | ## License
290 |
291 | MIT © [Shelf](https://shelf.io)
292 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "github>shelfio/renovate-config-public"
4 | ],
5 | "labels": [
6 | "backend"
7 | ],
8 | "ignoreDeps": [
9 | "cimg/node"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/src/environment.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | import {TestEnvironment} from 'jest-environment-node';
3 | import type {EnvironmentContext} from '@jest/environment';
4 | import type {JestEnvironmentConfig} from '@jest/environment';
5 |
6 | const debug = require('debug')('jest-dynamodb');
7 |
8 | module.exports = class DynamoDBEnvironment extends TestEnvironment {
9 | constructor(config: JestEnvironmentConfig, context: EnvironmentContext) {
10 | super(config, context);
11 | }
12 |
13 | async setup() {
14 | debug('Setup DynamoDB Test Environment');
15 |
16 | await super.setup();
17 | }
18 |
19 | async teardown() {
20 | debug('Teardown DynamoDB Test Environment');
21 |
22 | await super.teardown();
23 | }
24 |
25 | // @ts-ignore
26 | runScript(script) {
27 | // @ts-ignore
28 | return super.runScript(script);
29 | }
30 | };
31 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import {resolve} from 'path';
2 |
3 | export * from './types';
4 |
5 | export default {
6 | globalSetup: resolve(__dirname, './setup.js'),
7 | globalTeardown: resolve(__dirname, './teardown.js'),
8 | testEnvironment: resolve(__dirname, './environment.js'),
9 | };
10 |
--------------------------------------------------------------------------------
/src/setup.ts:
--------------------------------------------------------------------------------
1 | import DynamoDbLocal from 'dynamodb-local';
2 | import {DynamoDB} from '@aws-sdk/client-dynamodb';
3 | import type {ListTablesCommandOutput} from '@aws-sdk/client-dynamodb/dist-types/commands/ListTablesCommand';
4 | import type {argValues} from 'dynamodb-local';
5 | import type {CreateTableCommandInput} from '@aws-sdk/client-dynamodb';
6 | import getConfig from './utils/get-config';
7 | import deleteTables from './utils/delete-tables';
8 | import waitForLocalhost from './utils/wait-for-localhost';
9 | import getRelevantTables from './utils/get-relevant-tables';
10 |
11 | const debug = require('debug')('jest-dynamodb');
12 |
13 | const DEFAULT_PORT = 8000;
14 | const DEFAULT_HOST = 'localhost';
15 | const DEFAULT_OPTIONS: argValues[] = ['-sharedDb'];
16 |
17 | export default async function () {
18 | const {
19 | tables: newTables,
20 | clientConfig,
21 | installerConfig,
22 | port: port = DEFAULT_PORT,
23 | hostname: hostname = DEFAULT_HOST,
24 | options: options = DEFAULT_OPTIONS,
25 | } = await getConfig(debug);
26 |
27 | const dynamoDB = new DynamoDB({
28 | endpoint: `http://${hostname}:${port}`,
29 | tls: false,
30 | region: 'local-env',
31 | credentials: {
32 | accessKeyId: 'fakeMyKeyId',
33 | secretAccessKey: 'fakeSecretAccessKey',
34 | },
35 | ...clientConfig,
36 | });
37 |
38 | global.__DYNAMODB_CLIENT__ = dynamoDB;
39 |
40 | try {
41 | const promises: (Promise | Promise)[] = [
42 | dynamoDB.listTables({}),
43 | ];
44 |
45 | if (!global.__DYNAMODB__) {
46 | promises.push(waitForLocalhost(port, hostname));
47 | }
48 |
49 | const [TablesList] = await Promise.all(promises);
50 | const tableNames = TablesList?.TableNames;
51 |
52 | if (tableNames) {
53 | await deleteTables(dynamoDB, getRelevantTables(tableNames, newTables));
54 | }
55 | } catch (err) {
56 | // eslint-disable-next-line no-console
57 | debug(`fallback to launch DB due to ${err}`);
58 |
59 | if (installerConfig) {
60 | DynamoDbLocal.configureInstaller(installerConfig);
61 | }
62 |
63 | if (!global.__DYNAMODB__) {
64 | debug('spinning up a local ddb instance');
65 |
66 | global.__DYNAMODB__ = await DynamoDbLocal.launch(port, null, options);
67 | debug(`dynamodb-local started on port ${port}`);
68 |
69 | await waitForLocalhost(port, hostname);
70 | }
71 | }
72 | debug(`dynamodb-local is ready on port ${port}`);
73 |
74 | await createTables(dynamoDB, newTables);
75 | }
76 |
77 | function createTables(dynamoDB: DynamoDB, tables: CreateTableCommandInput[]) {
78 | return Promise.all(tables.map(table => dynamoDB.createTable(table)));
79 | }
80 |
--------------------------------------------------------------------------------
/src/teardown.ts:
--------------------------------------------------------------------------------
1 | import DynamoDbLocal from 'dynamodb-local';
2 | import type {JestArgs} from './types';
3 | import deleteTables from './utils/delete-tables';
4 | import getConfig from './utils/get-config';
5 | import getRelevantTables from './utils/get-relevant-tables';
6 |
7 | const debug = require('debug')('jest-dynamodb');
8 |
9 | export default async function (jestArgs: JestArgs) {
10 | // eslint-disable-next-line no-console
11 | debug('Teardown DynamoDB');
12 |
13 | if (global.__DYNAMODB__) {
14 | const watching = jestArgs.watch || jestArgs.watchAll;
15 |
16 | if (!watching) {
17 | await DynamoDbLocal.stopChild(global.__DYNAMODB__);
18 | }
19 | } else {
20 | const dynamoDB = global.__DYNAMODB_CLIENT__;
21 | const {tables: targetTables} = await getConfig(debug);
22 |
23 | const {TableNames: tableNames} = await dynamoDB.listTables({});
24 |
25 | if (tableNames?.length) {
26 | await deleteTables(dynamoDB, getRelevantTables(tableNames, targetTables));
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/types.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | import type {DynamoDB} from '@aws-sdk/client-dynamodb';
3 | import type {CreateTableCommandInput} from '@aws-sdk/client-dynamodb';
4 | import type {DynamoDBClientConfig} from '@aws-sdk/client-dynamodb';
5 | import type {ChildProcess} from 'child_process';
6 | import type {InstallerConfig} from 'dynamodb-local';
7 | import type {argValues} from 'dynamodb-local';
8 |
9 | declare global {
10 | var __DYNAMODB_CLIENT__: DynamoDB;
11 | var __DYNAMODB__: ChildProcess;
12 | }
13 |
14 | export type JestArgs = {
15 | bail: number;
16 | changedSince?: string;
17 | changedFilesWithAncestor: boolean;
18 | ci: boolean;
19 | collectCoverage: boolean;
20 | collectCoverageFrom: Array;
21 | collectCoverageOnlyFrom?: {
22 | [key: string]: boolean;
23 | };
24 | coverageDirectory: string;
25 | coveragePathIgnorePatterns?: Array;
26 | coverageProvider: object;
27 | coverageReporters: object;
28 | coverageThreshold?: object;
29 | detectLeaks: boolean;
30 | detectOpenHandles: boolean;
31 | expand: boolean;
32 | filter?: string;
33 | findRelatedTests: boolean;
34 | forceExit: boolean;
35 | json: boolean;
36 | globalSetup?: string;
37 | globalTeardown?: string;
38 | lastCommit: boolean;
39 | logHeapUsage: boolean;
40 | listTests: boolean;
41 | maxConcurrency: number;
42 | maxWorkers: number;
43 | noStackTrace: boolean;
44 | nonFlagArgs: Array;
45 | noSCM?: boolean;
46 | notify: boolean;
47 | notifyMode: object;
48 | outputFile?: string;
49 | onlyChanged: boolean;
50 | onlyFailures: boolean;
51 | passWithNoTests: boolean;
52 | projects: Array;
53 | replname?: string;
54 | reporters?: Array