-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
BREAKING CHANGE: Completely new API and integration.
- Loading branch information
Showing
43 changed files
with
4,274 additions
and
1,236 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,7 @@ on: | |
pull_request: | ||
|
||
env: | ||
NODE_VERSION: "18.18" | ||
NODE_VERSION: "16.20.2" | ||
|
||
jobs: | ||
lint: | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
18.18 | ||
lts/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,13 +5,9 @@ package-lock.json | |
dist/ | ||
.github/ | ||
|
||
# Testing | ||
coverage/ | ||
|
||
# Config | ||
tsconfig.* | ||
.eslintrc | ||
|
||
# Misc | ||
*.md | ||
.vscode/* | ||
*.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"javascript.preferences.importModuleSpecifierEnding": "js", | ||
"typescript.preferences.importModuleSpecifierEnding": "js" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,173 +1,154 @@ | ||
# Factoryse | ||
|
||
[![semantic-release: angular](https://img.shields.io/badge/semantic--release-angular-e10079?logo=semantic-release)](https://github.com/semantic-release/semantic-release) | ||
[![build](https://github.com/thekeogh/factoryse/actions/workflows/release.yml/badge.svg)](https://github.com/thekeogh/factoryse/actions)[![semantic-release: npm](https://img.shields.io/badge/semantic--release-npm-CB0000?logo=semantic-release)](https://github.com/semantic-release/semantic-release)[![node.js: >=16.20.2](https://img.shields.io/badge/node.js-%3E=16.20.2-036E02?logo=node.js)](https://nodejs.org) | ||
|
||
Factoryse is a test factory library built with [TypeScript](https://www.typescriptlang.org/), designed to streamline the creation of test data for your [Node.js](https://nodejs.org/) applications. Craft and manage test objects effortlessly, optimising the efficiency of your testing workflow. This project draws inspiration from Ruby's renowned [Factory Bot](https://github.com/thoughtbot/factory_bot), bringing similar ease and consistency to the world of TypeScript and Node.js testing. | ||
|
||
Factoryse is compatible with any modern testing suite, including [Vitest](https://vitest.dev/) and [Jest](https://jestjs.io/), without being tied to any specific one. | ||
|
||
---- | ||
Factoryse is designed to streamline the creation of test data, offering a simple and efficient approach to generate random (or deterministic, if preferred) data, thereby reducing redundancy in your testing setup. | ||
|
||
**Factoryse is currently in active development. Comprehensive details will be provided here in the near future.** | ||
|
||
---- | ||
Factoryse is compatible with any modern testing suite, including [Vitest](https://vitest.dev/) and [Jest](https://jestjs.io/), without being tied to any specific one. | ||
|
||
## Install | ||
|
||
```shell | ||
npm install --save-dev factoryse | ||
``` | ||
|
||
## Usage | ||
## How to use | ||
|
||
### Initialise | ||
### Defining a factory | ||
|
||
To begin, initialise your factory with the initial data for your schema: | ||
First, define your factory structure. This involves specifying the data model and creating the factory instance. | ||
|
||
```typescript | ||
import { Factory } from "factoryse"; | ||
|
||
// Define the User shape | ||
interface User { | ||
name: string; | ||
tags: string[]; | ||
address: { | ||
line1: string; | ||
} | ||
}; | ||
|
||
const user = new Factory<User>({ | ||
name: "Jon Jones", | ||
tags: ["male", "manager"], | ||
address: { | ||
line1: "66 Richmond Road", | ||
}, | ||
}); | ||
email: string; | ||
createdAt?: Date; | ||
} | ||
|
||
// Create the user factory | ||
const user = new Factory<User>(faker => ({ | ||
email: faker.internet.email() | ||
})); | ||
``` | ||
|
||
> The specification of the type `<User>` during Factory initialisation is entirely optional. | ||
> It's recommended to use [faker](https://fakerjs.dev/) during factory instantiation for generating diverse random data. | ||
### `spawn(count: number, data: (faker: Faker) => T)` | ||
### Generating entries | ||
|
||
To create multiple `Factory` instances simultaneously, you can utilise the `spawn` helper method, which generates an array of `Factory` instances: | ||
Generate a single entry: | ||
|
||
```typescript | ||
import { spawn } from "factoryse"; | ||
|
||
// Spawn an array of 5 Factory instances | ||
const users: Factory<User>[] = spawn(5, faker => ({ | ||
name: faker.person.fullName(), | ||
})); | ||
const factory = user.make(); | ||
// Generates: [{ email: "Jo65@gmail.com" }] | ||
``` | ||
|
||
When initialising your objects, you have the flexibility to use the [Faker](https://github.com/faker-js/faker) closure to easily generate random data. However, if you prefer, you can provide static strings or values instead of random data. | ||
To generate multiple entries: | ||
|
||
### `add(key: string, value: any)` | ||
```typescript | ||
const factory = user.make(2); | ||
// Generates: [{ email: "Jo65@gmail.com" }, { email: "Alv38@hotmail.com" }] | ||
``` | ||
|
||
This method adds a new key-value pair to the object. You have the flexibility to use dot notation for nesting keys within your schema: | ||
### Retrieving entries | ||
|
||
Leverage the `get()` action to access entries created by your Factory. Factoryse offers a variety of query methods to facilitate this retrieval process. | ||
|
||
```typescript | ||
// Adding a top-level key | ||
factory.add("foo", "bar"); | ||
// Retrieve all entries | ||
users.get().all(); // User[] | ||
|
||
// Adding a nested key | ||
factory.add("foo.bar", "baz") | ||
``` | ||
// Retrieve the first entry or the first (`n: number`) entries | ||
users.get().first(); // User[] | ||
users.get().first(n); // User[] | ||
|
||
For the sake of maintaining test stability, Factoryse will raise an error if the specified key already exists within the schema. To update existing values in your schema, it's advisable to use the `modify()` or `assign()` methods. | ||
// Retrieve the last entry or the last (`n: number`) entries | ||
users.get().last(); // User[] | ||
users.get().last(n); // User[] | ||
|
||
> Please note that Factoryse doesn't strictly adhere to the specified interface (e.g., `User`) and allows the addition of keys and values with any name and type to the object. This flexibility enables you to test scenarios with incorrect value types and additional keys that aren't defined in the interface. | ||
// Retrieve a random entry or multiple random (`n: number`) entries | ||
users.get().any(); // User[] | ||
users.get().any(n); // User[] | ||
|
||
### `modify(key: string, value: any)` | ||
// Retrieve a single entry by its index (`n: number`) | ||
users.get().at(n); // User | ||
|
||
This method modifies a value in the object. You have the flexibility to use dot notation for modifying nested keys within your schema: | ||
// Retrieve multiple entries by their index (`n: number[]`) | ||
users.get().pick(n); // User[] | ||
|
||
```typescript | ||
// Modify a top-level key | ||
factory.modify("foo", "bar"); | ||
// Retrieve multiple entries between indexes (`start: number, end: number`) | ||
users.get().between(start, end); // User[] | ||
|
||
// Modifying a nested key | ||
factory.modify("foo.bar", "baz") | ||
// Retrieve multiple entries by criteria (`c: Partial<User>`) | ||
users.get().by(c); // User[] | ||
``` | ||
|
||
For the sake of maintaining test stability, Factoryse will raise an error if the specified key does not exist within the schema. To add new values to your schema, it's advisable to use the `add()` or `assign()` methods. | ||
### Editing entries | ||
|
||
> It's important to be aware that Factoryse doesn't strictly adhere to the specified interface (e.g., `User`) and provides flexibility in modifying values of different types than what the interface demands. This flexibility allows you to conduct tests involving scenarios with incorrect value types and the addition of keys that aren't originally defined in the interface. | ||
Leverage the `set(source)` action to edit entries in your Factory. Factoryse offers a variety of query methods to facilitate this editing process. | ||
|
||
### `remove(key: string)` | ||
```typescript | ||
// Closure to apply modifications to entries | ||
const source = faker => ({ createdAt: faker.date.recent() }) | ||
|
||
This method removes key-value pairs from the object. You have the flexibility to use dot notation for removing nested keys within your schema: | ||
// Update all entries | ||
users.set(source).all(); // User[] | ||
|
||
```typescript | ||
// Remove a top-level key | ||
factory.remove("foo"); | ||
// Update the first entry or the first (`n: number`) entries | ||
users.set(source).first(); // User[] | ||
users.set(source).first(n); // User[] | ||
|
||
// Removing a nested key | ||
factory.remove("foo.bar") | ||
``` | ||
// Update the last entry or the last (`n: number`) entries | ||
users.set(source).last(); // User[] | ||
users.set(source).last(n); // User[] | ||
|
||
For the sake of maintaining test stability, Factoryse will raise an error if the specified key does not exist within the schema. | ||
// Update a random entry or multiple random (`n: number`) entries | ||
users.set(source).any(); // User[] | ||
users.set(source).any(n); // User[] | ||
|
||
### `assign(source: Record<string, any>, mergeArrays: boolean = false)` | ||
// Update a single entry by its index (`n: number`) | ||
users.set(source).at(n); // User | ||
|
||
This method facilitates the merging of the source schema with your object, with the source schema taking precedence over the target object. | ||
// Update multiple entries by their index (`n: number[]`) | ||
users.set(source).pick(n); // User[] | ||
|
||
```typescript | ||
// Merge a source object into your target | ||
factory.assign({ | ||
age: 42, | ||
tags: ["boss"], | ||
address: { | ||
line1: "23 Derton Road, London", | ||
country: "GB" | ||
} | ||
}); | ||
``` | ||
// Update multiple entries between indexes (`start: number, end: number`) | ||
users.set(source).between(start, end); // User[] | ||
|
||
When `mergeArrays` is set to `false` (the default), arrays will not be merged; instead, the source array will overwrite the target array. In this case, the result would be: | ||
|
||
```json | ||
{ | ||
"name": "Jon Jones", | ||
"age": 42, | ||
"tags": ["boss"], | ||
"address": { | ||
"line1": "23 Derton Road, London", | ||
"country": "GB" | ||
}, | ||
} | ||
// Update multiple entries by criteria (`c: Partial<User>`) | ||
users.set(source).by(c); // User[] | ||
``` | ||
|
||
If `mergeArrays` was set to `true`, the result would be: | ||
|
||
```json | ||
{ | ||
"name": "Jon Jones", | ||
"age": 42, | ||
"tags": ["male", "manager", "boss"], | ||
"address": { | ||
"line1": "23 Derton Road, London", | ||
"country": "GB" | ||
}, | ||
} | ||
``` | ||
### Deleting entries | ||
|
||
> It's worth noting that Factoryse offers flexibility by not strictly adhering to the specified interface (e.g., `User`). This flexibility allows you to test scenarios with different value types and the addition of keys not originally defined in the interface. | ||
Leverage the `delete()` action to delete entries from your Factory. Factoryse offers a variety of query methods to facilitate this deleting process. | ||
|
||
### `reset()` | ||
```typescript | ||
// Delete all entries | ||
users.delete().all(); // User[] | ||
|
||
This method restores the target object to its initial state as defined during instantiation. Be cautious as this action permanently erases any changes made to the object since its creation. | ||
// Delete the first entry or the first (`n: number`) entries | ||
users.delete().first(); // User[] | ||
users.delete().first(n); // User[] | ||
|
||
```typescript | ||
// Reset the target back to its original state | ||
factory.reset(); | ||
``` | ||
// Delete the last entry or the last (`n: number`) entries | ||
users.delete().last(); // User[] | ||
users.delete().last(n); // User[] | ||
|
||
### `get()` | ||
// Delete a random entry or multiple random (`n: number`) entries | ||
users.delete().any(); // User[] | ||
users.delete().any(n); // User[] | ||
|
||
This method provides you with the resulting schema object after all modifications have been applied. | ||
// Delete a single entry by its index (`n: number`) | ||
users.delete().at(n); // User | ||
|
||
```typescript | ||
// Retrieve the current schema | ||
factory.get() | ||
``` | ||
// Delete multiple entries by their index (`n: number[]`) | ||
users.delete().pick(n); // User[] | ||
|
||
// Delete multiple entries between indexes (`start: number, end: number`) | ||
users.delete().between(start, end); // User[] | ||
|
||
// Delete multiple entries by criteria (`c: Partial<User>`) | ||
users.delete().by(c); // User[] | ||
``` |
Oops, something went wrong.