mirror of
				https://github.com/strapi/strapi.git
				synced 2025-11-04 03:43:34 +00:00 
			
		
		
		
	Merge branch 'master' into patch-1
This commit is contained in:
		
						commit
						b555c898ba
					
				@ -28,7 +28,7 @@
 | 
			
		||||
 | 
			
		||||
<p align="center">
 | 
			
		||||
  <a href="https://www.npmjs.org/package/strapi">
 | 
			
		||||
    <img src="https://img.shields.io/npm/v/strapi/beta.svg" alt="NPM Version" />
 | 
			
		||||
    <img src="https://img.shields.io/npm/v/strapi/latest.svg" alt="NPM Version" />
 | 
			
		||||
  </a>
 | 
			
		||||
  <a href="https://www.npmjs.org/package/strapi">
 | 
			
		||||
    <img src="https://img.shields.io/npm/dm/strapi.svg" alt="Monthly download on NPM" />
 | 
			
		||||
 | 
			
		||||
@ -213,6 +213,7 @@ module.exports = {
 | 
			
		||||
            '/v3.x/guides/registering-a-field-in-admin',
 | 
			
		||||
            '/v3.x/guides/client',
 | 
			
		||||
            '/v3.x/guides/update-version',
 | 
			
		||||
            '/v3.x/guides/unit-testing',
 | 
			
		||||
          ],
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
@ -262,25 +262,7 @@ yarn add strapi-provider-upload-google-cloud-storage
 | 
			
		||||
 | 
			
		||||
Deploy so that the server app includes the dependency from `package.json`.
 | 
			
		||||
 | 
			
		||||
Create a Google service account key.
 | 
			
		||||
 | 
			
		||||
<https://console.cloud.google.com/apis/credentials/serviceaccountkey>
 | 
			
		||||
 | 
			
		||||
Save the JSON credentials file.
 | 
			
		||||
 | 
			
		||||
Plugins > File Upload > Settings > Production tab
 | 
			
		||||
 | 
			
		||||
By default `localhost` is selected. Select the `Google Cloud Storage` plugin.
 | 
			
		||||
 | 
			
		||||
Copy the JSON key and set the regions.
 | 
			
		||||
 | 
			
		||||
Open the `Cloud Console > Storage > Browser` menu.
 | 
			
		||||
 | 
			
		||||
Copy the bucket name to the plugin settings, the default is the app ID, such as `myapi-123456.appspot.com`.
 | 
			
		||||
 | 
			
		||||
(Note that the `Access control` setting of the bucket has to be `Fine-grained`, which is the default.)
 | 
			
		||||
 | 
			
		||||
Click `Save`, and it's ready to go!
 | 
			
		||||
Follow the [documentation of the plugin](https://github.com/Lith/strapi-provider-upload-google-cloud-storage/blob/master/README.md) for the full configuration.
 | 
			
		||||
 | 
			
		||||
### Post-setup configuration
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,7 @@ Deploying **databases** along with Strapi is covered in the [Databases Guide](..
 | 
			
		||||
Manual guides for deployment on various platforms, for One-click and docker please see the [installation](./installation) guides.
 | 
			
		||||
 | 
			
		||||
<div>
 | 
			
		||||
	<InstallLink link="../deployment/amazon-aws">
 | 
			
		||||
	<InstallLink link="../deployment/amazon-aws.html">
 | 
			
		||||
    <template #icon>
 | 
			
		||||
    <svg width="64" height="64" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><g fill="#fff" fill-rule="evenodd"><path d="M15.63 31.388l-7.135-2.56V18.373l7.135 2.43zm1.3 0l7.135-2.56V18.373l-7.135 2.432zm-7.7-13.8l7.2-2.033 6.696 2.16-6.696 2.273zm-2.092-.8L0 14.22V3.75l7.135 2.43zm1.307 0l7.135-2.56V3.75L8.443 6.192zm-7.7-13.8l7.2-2.043 6.696 2.16-6.696 2.273zm23.052 13.8l-7.135-2.56V3.75l7.135 2.43zm1.3 0l7.135-2.56V3.75l-7.135 2.43zm-7.7-13.8l7.2-2.033 6.696 2.16-6.696 2.273z" fill-rule="nonzero"></path></g></svg>
 | 
			
		||||
    </template>
 | 
			
		||||
@ -23,7 +23,7 @@ Manual guides for deployment on various platforms, for One-click and docker plea
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div>
 | 
			
		||||
	<InstallLink link="../deployment/azure">
 | 
			
		||||
	<InstallLink link="../deployment/azure.html">
 | 
			
		||||
    <template #icon>
 | 
			
		||||
    <svg width="100" height="77.43" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19.68 15.24"><path d="M9.105 14.43l4.642-.82.043-.01-2.387-2.84a403.945 403.945 0 0 1-2.387-2.853c0-.014 2.465-6.802 2.479-6.826.004-.008 1.682 2.888 4.066 7.02l4.09 7.09.031.054-7.587-.001-7.587-.001 4.597-.812zM0 13.566c0-.004 1.125-1.957 2.5-4.34L5 4.893l2.913-2.445C9.515 1.104 10.83.002 10.836 0a.512.512 0 0 1-.047.118L7.625 6.903l-3.107 6.663-2.259.003c-1.242.002-2.259 0-2.259-.004z" fill="#fff"/></svg>
 | 
			
		||||
    </template>
 | 
			
		||||
@ -35,7 +35,7 @@ Manual guides for deployment on various platforms, for One-click and docker plea
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div>
 | 
			
		||||
	<InstallLink link="../deployment/digitalocean">
 | 
			
		||||
	<InstallLink link="../deployment/digitalocean.html">
 | 
			
		||||
		<template #icon>
 | 
			
		||||
			<svg width="178" height="177" viewBox="0 0 178 177" xmlns="http://www.w3.org/2000/svg"><g fill="#fff" fill-rule="evenodd"><path d="M89 176.5v-34.2c36.2 0 64.3-35.9 50.4-74-5.1-14-16.4-25.3-30.5-30.4-38.1-13.8-74 14.2-74 50.4H.8C.8 30.6 56.6-14.4 117.1 4.5c26.4 8.3 47.5 29.3 55.7 55.7 18.9 60.5-26.1 116.3-83.8 116.3z" fill-rule="nonzero"></path><path d="M89.1 142.5H55v-34.1h34.1zM55 168.6H28.9v-26.1H55zM28.9 142.5H7v-21.9h21.9v21.9z"></path></g></svg>
 | 
			
		||||
		</template>
 | 
			
		||||
@ -47,7 +47,7 @@ Manual guides for deployment on various platforms, for One-click and docker plea
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div>
 | 
			
		||||
	<InstallLink link="../deployment/google-app-engine">
 | 
			
		||||
	<InstallLink link="../deployment/google-app-engine.html">
 | 
			
		||||
		<template #icon>
 | 
			
		||||
			<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" version="1.1"><path d="M6.969 3L4.094 8.188l1.468 2.624L8.438 6h10.25L17 3zm8.75 4l2.969 4.906L13.625 21H17l5-9-2.781-5zM12 8c-2.207 0-4 1.793-4 4s1.793 4 4 4 4-1.793 4-4-1.793-4-4-4zM3.531 9.219L2 12l4.969 9H12.5l1.656-3h-5.75zM12 10c1.102 0 2 .898 2 2 0 1.102-.898 2-2 2-1.102 0-2-.898-2-2 0-1.102.898-2 2-2z" fill="#fff"/></svg>
 | 
			
		||||
		</template>
 | 
			
		||||
@ -59,7 +59,7 @@ Manual guides for deployment on various platforms, for One-click and docker plea
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div>
 | 
			
		||||
	<InstallLink link="../deployment/heroku">
 | 
			
		||||
	<InstallLink link="../deployment/heroku.html">
 | 
			
		||||
    <template #icon>
 | 
			
		||||
    <svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 5.12 5.12" preserveAspectRatio="xMinYMin meet"><path d="M3.068 4.415V2.382s.132-.487-1.63.2C1.436 2.6 1.436.7 1.436.7L2.01.697v1.2s1.61-.635 1.61.48v2.026h-.555zm.328-2.986h-.6c.22-.27.42-.73.42-.73h.63s-.108.3-.44.73zm-1.95 2.982V3.254l.58.58-.58.58z" fill="#fff"/></svg>
 | 
			
		||||
    </template>
 | 
			
		||||
@ -75,7 +75,7 @@ Manual guides for deployment on various platforms, for One-click and docker plea
 | 
			
		||||
Additional guides for optional software additions that compliment or improve the deployment process when using Strapi in a production or production-like environment.
 | 
			
		||||
 | 
			
		||||
<div>
 | 
			
		||||
	<InstallLink link="../deployment/nginx-proxy">
 | 
			
		||||
	<InstallLink link="../deployment/nginx-proxy.html">
 | 
			
		||||
    <template #icon>
 | 
			
		||||
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="-35.5 26 32 32" width="64" height="64"><path d="M-33.442 42.023v-7.637a.68.68 0 0 1 .385-.651l13.173-7.608c.237-.148.503-.178.74-.03l13.232 7.637a.71.71 0 0 1 .355.651V49.63a.71.71 0 0 1-.355.651l-11.367 6.57a56.27 56.27 0 0 1-1.806 1.036c-.266.148-.533.148-.8 0l-13.202-7.608c-.237-.148-.355-.326-.355-.622v-7.637z" fill="#fff"/><path d="M-24.118 39.18v8.9c0 1.006-.8 1.894-1.865 1.865-.65-.03-1.154-.296-1.5-.858-.178-.266-.237-.562-.237-.888V35.836c0-.83.503-1.42 1.154-1.687s1.302-.207 1.954 0c.622.178 1.095.562 1.5 1.036l7.874 9.443c.03.03.06.09.118.148v-9c0-.947.65-1.687 1.57-1.776 1.154-.148 1.924.68 2.042 1.54v12.6c0 .7-.326 1.214-.918 1.54-.444.237-.918.296-1.42.266a3.23 3.23 0 0 1-1.954-.829c-.296-.266-.503-.592-.77-.888l-7.49-8.97c0-.03-.03-.06-.06-.09z" fill="#3498DB"/></svg>
 | 
			
		||||
    </template>
 | 
			
		||||
 | 
			
		||||
@ -215,7 +215,10 @@ To improve the Developer Experience when developing or using the administration
 | 
			
		||||
 | 
			
		||||
### Exceptions
 | 
			
		||||
 | 
			
		||||
- `uid` — This field type allows a `targetField` key. The value is the name of an attribute thas has `string` of `text` type.
 | 
			
		||||
**uid**
 | 
			
		||||
 | 
			
		||||
- `targetField`(string) — The value is the name of an attribute thas has `string` of `text` type.
 | 
			
		||||
- `options` (string) — The value is a set of options passed to [the underlying `uid` generator](https://github.com/sindresorhus/slugify). A caveat is that the resulting `uid` must abide to the following RegEx `/^[A-Za-z0-9-_.~]*$`.
 | 
			
		||||
 | 
			
		||||
### Example
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -237,34 +237,7 @@ yarn add strapi-provider-upload-google-cloud-storage
 | 
			
		||||
 | 
			
		||||
Deploy so that the server app includes the dependency from `package.json`.
 | 
			
		||||
 | 
			
		||||
Create a Google service account key.
 | 
			
		||||
 | 
			
		||||
<https://console.cloud.google.com/apis/credentials/serviceaccountkey>
 | 
			
		||||
 | 
			
		||||
Save the JSON credentials file.
 | 
			
		||||
 | 
			
		||||
Open the JSON credentials file, copy all the content and set an environment variable named as `GCS_SERVICE_ACCOUNT`
 | 
			
		||||
 | 
			
		||||
Create a settings.json file in below path
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
./extensions/upload/config/settings.json
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Paste in the code below, replace `###YOUR BUCKET NAME###` with your bucket name such as `myapi-123456.appspot.com`
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
{
 | 
			
		||||
  "provider": "google-cloud-storage",
 | 
			
		||||
  "providerOptions": {
 | 
			
		||||
    "serviceAccount": "${process.env.GCS_SERVICE_ACCOUNT}",
 | 
			
		||||
    "bucketName": "###YOUR BUCKET NAME###",
 | 
			
		||||
    "baseUrl": "https://storage.googleapis.com/###YOUR BUCKET NAME###"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
(Note that the `Access control` setting of the bucket has to be `Fine-grained`, which is the default.)
 | 
			
		||||
Follow the [documentation of the plugin](https://github.com/Lith/strapi-provider-upload-google-cloud-storage/blob/master/README.md) for the full configuration.
 | 
			
		||||
 | 
			
		||||
### Post-setup configuration
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										340
									
								
								docs/v3.x/guides/unit-testing.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										340
									
								
								docs/v3.x/guides/unit-testing.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,340 @@
 | 
			
		||||
---
 | 
			
		||||
sidebarDepth: 2
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
# Unit testing
 | 
			
		||||
 | 
			
		||||
In this guide we will see how you can run basic unit tests for a Strapi application using a testing framework.
 | 
			
		||||
 | 
			
		||||
::: tip
 | 
			
		||||
In this example we will use [Jest](https://jestjs.io/) Testing Framework with a focus on simplicity and
 | 
			
		||||
[Supertest](https://github.com/visionmedia/supertest) Super-agent driven library for testing node.js HTTP servers using a fluent API
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
## Install test tools
 | 
			
		||||
 | 
			
		||||
`Jest` contains a set of guidelines or rules used for creating and designing test cases - a combination of practices and tools that are designed to help testers test more efficiently.
 | 
			
		||||
 | 
			
		||||
`Supertest` allows you to test all the `api` routes as they were instances of [http.Server](https://nodejs.org/api/http.html#http_class_http_server)
 | 
			
		||||
 | 
			
		||||
:::: tabs
 | 
			
		||||
 | 
			
		||||
::: tab yarn
 | 
			
		||||
`yarn add jest supertest`
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
::: tab npm
 | 
			
		||||
`npm install jest supertest`
 | 
			
		||||
:::
 | 
			
		||||
::::
 | 
			
		||||
 | 
			
		||||
Once this is done add this to `package.json` file
 | 
			
		||||
 | 
			
		||||
add `test` command to `scripts` section
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "develop": "strapi develop",
 | 
			
		||||
    "start": "strapi start",
 | 
			
		||||
    "build": "strapi build",
 | 
			
		||||
    "strapi": "strapi",
 | 
			
		||||
    "test": "jest --forceExit --detectOpenHandles"
 | 
			
		||||
  },
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
and add those line at the bottom of file
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
  "jest": {
 | 
			
		||||
    "testPathIgnorePatterns": [
 | 
			
		||||
      "/node_modules/",
 | 
			
		||||
      ".tmp",
 | 
			
		||||
      ".cache"
 | 
			
		||||
    ],
 | 
			
		||||
    "testEnvironment": "node"
 | 
			
		||||
  }
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Those will inform `Jest` not to look for test inside the folder where it shouldn't.
 | 
			
		||||
 | 
			
		||||
## Introduction
 | 
			
		||||
 | 
			
		||||
### Testing environment
 | 
			
		||||
 | 
			
		||||
Test framework must have a clean empty environment to perform valid test and also not to interfere with current database.
 | 
			
		||||
 | 
			
		||||
Once `jest` is running it uses the `test` [enviroment](../concepts/configurations.md#environments) (switching `NODE_ENV` to `test`)
 | 
			
		||||
so we need to create a special environment setting for this purpose.
 | 
			
		||||
Create a new config for test env `./config/env/test/database.json` and add the following value `"filename": ".tmp/test.db"` - the reason of that is that we want to have a separate sqlite database for tests, so our test will not touch real data.
 | 
			
		||||
This file will be temporary, each time test is finished, we will remove that file that every time tests are run on the clean database.
 | 
			
		||||
The whole file will look like this:
 | 
			
		||||
 | 
			
		||||
**Path —** `./config/env/test/database.json`
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
  "defaultConnection": "default",
 | 
			
		||||
  "connections": {
 | 
			
		||||
    "default": {
 | 
			
		||||
      "connector": "bookshelf",
 | 
			
		||||
      "settings": {
 | 
			
		||||
        "client": "sqlite",
 | 
			
		||||
        "filename": ".tmp/test.db"
 | 
			
		||||
      },
 | 
			
		||||
      "options": {
 | 
			
		||||
        "useNullAsDefault": true,
 | 
			
		||||
        "pool": {
 | 
			
		||||
          "min": 0,
 | 
			
		||||
          "max": 15
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Strapi instance
 | 
			
		||||
 | 
			
		||||
In order to test anything we need to have a strapi instance that runs in the testing eviroment,
 | 
			
		||||
bascially we want to get instance of strapi app as object, similar like creating an instance for [process manager](process-manager).
 | 
			
		||||
 | 
			
		||||
These tasks require adding some files - let's create a folder `tests` where all the tests will be put and inside it, next to folder `helpers` where main Strapi helper will be in file strapi.js.
 | 
			
		||||
 | 
			
		||||
**Path —** `./tests/helpers/strapi.js`
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const Strapi = require('strapi');
 | 
			
		||||
const http = require('http');
 | 
			
		||||
 | 
			
		||||
let instance;
 | 
			
		||||
 | 
			
		||||
async function setupStrapi() {
 | 
			
		||||
  if (!instance) {
 | 
			
		||||
    /** the following code in copied from `./node_modules/strapi/lib/Strapi.js` */
 | 
			
		||||
    await Strapi().load();
 | 
			
		||||
    instance = strapi; // strapi is global now
 | 
			
		||||
    await instance.app
 | 
			
		||||
      .use(instance.router.routes()) // populate KOA routes
 | 
			
		||||
      .use(instance.router.allowedMethods()); // populate KOA methods
 | 
			
		||||
 | 
			
		||||
    instance.server = http.createServer(instance.app.callback());
 | 
			
		||||
  }
 | 
			
		||||
  return instance;
 | 
			
		||||
}
 | 
			
		||||
module.exports = { setupStrapi };
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Test strapi instance
 | 
			
		||||
 | 
			
		||||
We need a main entry file for our tests, one that will also test our helper file.
 | 
			
		||||
 | 
			
		||||
**Path —** `./tests/app.test.js`
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const fs = require('fs');
 | 
			
		||||
const { setupStrapi } = require('./helpers/strapi');
 | 
			
		||||
 | 
			
		||||
/** this code is called once before any test is called */
 | 
			
		||||
beforeAll(async (done) => {
 | 
			
		||||
  await setupStrapi(); // singleton so it can be called many times
 | 
			
		||||
  done();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
/** this code is called once before all the tested are finished */
 | 
			
		||||
afterAll(async (done) => {
 | 
			
		||||
  const dbSettings = strapi.config.get('database.connections.default.settings');
 | 
			
		||||
 | 
			
		||||
  //delete test database after all tests
 | 
			
		||||
  if (dbSettings && dbSettings.filename) {
 | 
			
		||||
    const tmpDbFile = `${__dirname}/../${dbSettings.filename}`;
 | 
			
		||||
    if (fs.existsSync(tmpDbFile)) {
 | 
			
		||||
      fs.unlinkSync(tmpDbFile);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  done();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
it('strapi is defined', async (done) => {
 | 
			
		||||
  expect(strapi).toBeDefined();
 | 
			
		||||
  done();
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Actually this is all we need for writing unit tests. Just run `yarn test` and see a result of your first test
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
yarn run v1.13.0
 | 
			
		||||
$ jest
 | 
			
		||||
 PASS  tests/app.test.js
 | 
			
		||||
  ✓ strapi is defined (2 ms)
 | 
			
		||||
 | 
			
		||||
Test Suites: 1 passed, 1 total
 | 
			
		||||
Tests:       1 passed, 1 total
 | 
			
		||||
Snapshots:   0 total
 | 
			
		||||
Time:        4.187 s
 | 
			
		||||
Ran all test suites.
 | 
			
		||||
✨  Done in 5.73s.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Testing basic endpoint controller.
 | 
			
		||||
 | 
			
		||||
::: tip
 | 
			
		||||
In the example we'll use and example `Hello world` `/hello` endpoint from [controllers](../concepts/controllers.md#example) section.
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
Some might say that API tests are not unit but limited integration tests, regardless of nomenclature, let's continue with testing first endpoint.
 | 
			
		||||
 | 
			
		||||
We'll test if our endpoint works properly and route `/hello` does return `Hello World`
 | 
			
		||||
 | 
			
		||||
Let's create a separate test file were `supertest` will be used to check if endpoint works as expected.
 | 
			
		||||
 | 
			
		||||
**Path —** `./tests/hello/index.js`
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const request = require('supertest');
 | 
			
		||||
 | 
			
		||||
it('should return hello world', async (done) => {
 | 
			
		||||
  await request(strapi.server) // app server is an instance of Class: http.Server
 | 
			
		||||
    .get('/hello')
 | 
			
		||||
    .expect(200) // Expect response http code 200
 | 
			
		||||
    .then((data) => {
 | 
			
		||||
      expect(data.text).toBe('Hello World!'); // expect the response text
 | 
			
		||||
    });
 | 
			
		||||
  done();
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Then include this code to `./tests/app.test.js` at the bottom of that file
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
require('./hello');
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
and run `yarn test` which should return
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
➜  my-project yarn test
 | 
			
		||||
yarn run v1.13.0
 | 
			
		||||
$ jest --detectOpenHandles
 | 
			
		||||
 PASS  tests/app.test.js (5.742 s)
 | 
			
		||||
  ✓ strapi is defined (4 ms)
 | 
			
		||||
  ✓ should return hello world (208 ms)
 | 
			
		||||
 | 
			
		||||
[2020-05-22T14:37:38.018Z] debug GET /hello (58 ms) 200
 | 
			
		||||
Test Suites: 1 passed, 1 total
 | 
			
		||||
Tests:       2 passed, 2 total
 | 
			
		||||
Snapshots:   0 total
 | 
			
		||||
Time:        6.635 s, estimated 7 s
 | 
			
		||||
Ran all test suites.
 | 
			
		||||
✨  Done in 9.09s.
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Testing `auth` endpoint controller.
 | 
			
		||||
 | 
			
		||||
In this scenario we'll test authentication login endpoint with two tests
 | 
			
		||||
 | 
			
		||||
1. Test `/auth/local` that should login user and return `jwt` token
 | 
			
		||||
2. Test `/users/me` that should return users data based on `Authorization` header
 | 
			
		||||
 | 
			
		||||
**Path —** `./tests/user/index.js`
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const request = require('supertest');
 | 
			
		||||
 | 
			
		||||
// user mock data
 | 
			
		||||
const mockUserData = {
 | 
			
		||||
  username: 'tester',
 | 
			
		||||
  email: 'tester@strapi.com',
 | 
			
		||||
  provider: 'local',
 | 
			
		||||
  password: '1234abc',
 | 
			
		||||
  confirmed: true,
 | 
			
		||||
  blocked: null,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
it('should login user and return jwt token', async (done) => {
 | 
			
		||||
  /** Creates a new user and save it to the database */
 | 
			
		||||
  await strapi.plugins['users-permissions'].services.user.add({
 | 
			
		||||
    ...mockUserData,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  await request(strapi.server) // app server is an instance of Class: http.Server
 | 
			
		||||
    .post('/auth/local')
 | 
			
		||||
    .set('accept', 'application/json')
 | 
			
		||||
    .set('Content-Type', 'application/json')
 | 
			
		||||
    .send({
 | 
			
		||||
      identifier: mockUserData.email,
 | 
			
		||||
      password: mockUserData.password,
 | 
			
		||||
    })
 | 
			
		||||
    .expect('Content-Type', /json/)
 | 
			
		||||
    .expect(200)
 | 
			
		||||
    .then((data) => {
 | 
			
		||||
      expect(data.body.jwt).toBeDefined();
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
  done();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
it('should return users data for authenticated user', async (done) => {
 | 
			
		||||
  /** Gets the default user role */
 | 
			
		||||
  const defaultRole = await strapi.query('role', 'users-permissions').findOne({}, []);
 | 
			
		||||
 | 
			
		||||
  const role = defaultRole ? defaultRole.id : null;
 | 
			
		||||
 | 
			
		||||
  /** Creates a new user an push to database */
 | 
			
		||||
  const user = await strapi.plugins['users-permissions'].services.user.add({
 | 
			
		||||
    ...mockUserData,
 | 
			
		||||
    username: 'tester2',
 | 
			
		||||
    email: 'tester2@strapi.com',
 | 
			
		||||
    role,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const jwt = strapi.plugins['users-permissions'].services.jwt.issue({
 | 
			
		||||
    id: user.id,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  await request(strapi.server) // app server is an instance of Class: http.Server
 | 
			
		||||
    .get('/users/me')
 | 
			
		||||
    .set('accept', 'application/json')
 | 
			
		||||
    .set('Content-Type', 'application/json')
 | 
			
		||||
    .set('Authorization', 'Bearer ' + jwt)
 | 
			
		||||
    .expect('Content-Type', /json/)
 | 
			
		||||
    .expect(200)
 | 
			
		||||
    .then((data) => {
 | 
			
		||||
      expect(data.body).toBeDefined();
 | 
			
		||||
      expect(data.body.id).toBe(user.id);
 | 
			
		||||
      expect(data.body.username).toBe(user.username);
 | 
			
		||||
      expect(data.body.email).toBe(user.email);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
  done();
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Then include this code to `./tests/app.test.js` at the bottom of that file
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
require('./user');
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
All the tests above should return an console output like
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
➜  my-project git:(master) yarn test
 | 
			
		||||
 | 
			
		||||
yarn run v1.13.0
 | 
			
		||||
$ jest --forceExit --detectOpenHandles
 | 
			
		||||
[2020-05-27T08:30:30.811Z] debug GET /hello (10 ms) 200
 | 
			
		||||
[2020-05-27T08:30:31.864Z] debug POST /auth/local (891 ms) 200
 | 
			
		||||
 PASS  tests/app.test.js (6.811 s)
 | 
			
		||||
  ✓ strapi is defined (3 ms)
 | 
			
		||||
  ✓ should return hello world (54 ms)
 | 
			
		||||
  ✓ should login user and return jwt token (1049 ms)
 | 
			
		||||
  ✓ should return users data for authenticated user (163 ms)
 | 
			
		||||
 | 
			
		||||
Test Suites: 1 passed, 1 total
 | 
			
		||||
Tests:       4 passed, 4 total
 | 
			
		||||
Snapshots:   0 total
 | 
			
		||||
Time:        6.874 s, estimated 9 s
 | 
			
		||||
Ran all test suites.
 | 
			
		||||
✨  Done in 8.40s.
 | 
			
		||||
```
 | 
			
		||||
@ -132,6 +132,43 @@ describe('Test uid service', () => {
 | 
			
		||||
      expect(uidWithEmptyTarget).toBe('my-test-model');
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    test('Uses options for generation', async () => {
 | 
			
		||||
      global.strapi = {
 | 
			
		||||
        contentTypes: {
 | 
			
		||||
          'my-model': {
 | 
			
		||||
            modelName: 'myTestModel',
 | 
			
		||||
            attributes: {
 | 
			
		||||
              title: {
 | 
			
		||||
                type: 'string',
 | 
			
		||||
              },
 | 
			
		||||
              slug: {
 | 
			
		||||
                type: 'uid',
 | 
			
		||||
                targetField: 'title',
 | 
			
		||||
                options: { lowercase: false },
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        db: {
 | 
			
		||||
          query() {
 | 
			
		||||
            return {
 | 
			
		||||
              find: async () => [],
 | 
			
		||||
            };
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
      const uid = await uidService.generateUIDField({
 | 
			
		||||
        contentTypeUID: 'my-model',
 | 
			
		||||
        field: 'slug',
 | 
			
		||||
        data: {
 | 
			
		||||
          title: 'Test title',
 | 
			
		||||
        },
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      expect(uid).toBe('Test-title');
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    test('Ignores minLength attribute (should be handle by the user)', async () => {
 | 
			
		||||
      global.strapi = {
 | 
			
		||||
        contentTypes: {
 | 
			
		||||
 | 
			
		||||
@ -8,21 +8,21 @@ module.exports = {
 | 
			
		||||
    const contentType = strapi.contentTypes[contentTypeUID];
 | 
			
		||||
    const { attributes } = contentType;
 | 
			
		||||
 | 
			
		||||
    const { targetField, default: defaultValue } = attributes[field];
 | 
			
		||||
    const { targetField, default: defaultValue, options } = attributes[field];
 | 
			
		||||
    const targetValue = _.get(data, targetField);
 | 
			
		||||
 | 
			
		||||
    if (!_.isEmpty(targetValue)) {
 | 
			
		||||
      return this.findUniqueUID({
 | 
			
		||||
        contentTypeUID,
 | 
			
		||||
        field,
 | 
			
		||||
        value: slugify(targetValue),
 | 
			
		||||
        value: slugify(targetValue, options),
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return this.findUniqueUID({
 | 
			
		||||
      contentTypeUID,
 | 
			
		||||
      field,
 | 
			
		||||
      value: slugify(defaultValue || contentType.modelName),
 | 
			
		||||
      value: slugify(defaultValue || contentType.modelName, options),
 | 
			
		||||
    });
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -86,6 +86,19 @@ const getTypeShape = (attribute, { modelType, attributes } = {}) => {
 | 
			
		||||
          .test(isValidUID),
 | 
			
		||||
        minLength: validators.minLength,
 | 
			
		||||
        maxLength: validators.maxLength.max(256).test(maxLengthIsGreaterThanOrEqualToMinLength),
 | 
			
		||||
        options: yup.object().shape({
 | 
			
		||||
          separator: yup.string(),
 | 
			
		||||
          lowercase: yup.boolean(),
 | 
			
		||||
          decamelize: yup.boolean(),
 | 
			
		||||
          customReplacements: yup.array().of(
 | 
			
		||||
            yup
 | 
			
		||||
              .array()
 | 
			
		||||
              .of(yup.string())
 | 
			
		||||
              .min(2)
 | 
			
		||||
              .max(2)
 | 
			
		||||
          ),
 | 
			
		||||
          preserveLeadingUnderscore: yup.boolean(),
 | 
			
		||||
        }),
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user