@rvoh/psychic-spec-helpers 0.6.1 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/spec/features/check.spec.js +12 -0
- package/dist/esm/spec/features/uncheck.spec.js +23 -0
- package/dist/esm/spec/unit/OpenapiSpecRequest/delete.spec.js +29 -0
- package/dist/esm/spec/unit/OpenapiSpecRequest/get.spec.js +35 -0
- package/dist/esm/spec/unit/OpenapiSpecRequest/patch.spec.js +32 -0
- package/dist/esm/spec/unit/OpenapiSpecRequest/post.spec.js +30 -0
- package/dist/esm/spec/unit/OpenapiSpecRequest/put.spec.js +32 -0
- package/dist/esm/spec/unit/OpenapiSpecSession/delete.spec.js +17 -0
- package/dist/esm/spec/unit/OpenapiSpecSession/get.spec.js +24 -0
- package/dist/esm/spec/unit/OpenapiSpecSession/patch.spec.js +20 -0
- package/dist/esm/spec/unit/OpenapiSpecSession/post.spec.js +18 -0
- package/dist/esm/spec/unit/OpenapiSpecSession/put.spec.js +20 -0
- package/dist/esm/spec/unit/specRequest/helpers/fillOpenapiUrlParams.spec.js +11 -0
- package/dist/esm/src/feature/error-messages/emptyForAttribute.js +12 -0
- package/dist/esm/src/feature/error-messages/missingInputToMatchForAttribute.js +10 -0
- package/dist/esm/src/feature/internal/captureAttributeFromClosestElementWithType.js +26 -0
- package/dist/esm/src/feature/matchers/toCheck.js +21 -1
- package/dist/esm/src/feature/matchers/toNotMatchTextContent.js +1 -1
- package/dist/esm/src/feature/matchers/toUncheck.js +21 -3
- package/dist/esm/src/index.js +2 -0
- package/dist/esm/src/unit/OpenapiSpecRequest.js +103 -0
- package/dist/esm/src/unit/OpenapiSpecSession.js +54 -0
- package/dist/esm/src/unit/helpers/fillOpenapiParams.js +8 -0
- package/dist/esm/src/unit/helpers/openapiTypeHelpers.js +1 -0
- package/dist/esm/src/unit/helpers/typeHelpers.js +1 -0
- package/dist/esm/test-app/src/app/controllers/UserController.js +84 -0
- package/dist/esm/test-app/src/app/controllers/UsersController.js +107 -0
- package/dist/esm/test-app/src/conf/app.js +3 -0
- package/dist/esm/test-app/src/conf/routes.js +9 -0
- package/dist/types/spec/unit/OpenapiSpecRequest/delete.spec.d.ts +1 -0
- package/dist/types/spec/unit/OpenapiSpecRequest/get.spec.d.ts +1 -0
- package/dist/types/spec/unit/OpenapiSpecRequest/patch.spec.d.ts +1 -0
- package/dist/types/spec/unit/OpenapiSpecRequest/post.spec.d.ts +1 -0
- package/dist/types/spec/unit/OpenapiSpecRequest/put.spec.d.ts +1 -0
- package/dist/types/spec/unit/OpenapiSpecSession/delete.spec.d.ts +1 -0
- package/dist/types/spec/unit/OpenapiSpecSession/get.spec.d.ts +1 -0
- package/dist/types/spec/unit/OpenapiSpecSession/patch.spec.d.ts +1 -0
- package/dist/types/spec/unit/OpenapiSpecSession/post.spec.d.ts +1 -0
- package/dist/types/spec/unit/OpenapiSpecSession/put.spec.d.ts +1 -0
- package/dist/types/spec/unit/specRequest/helpers/fillOpenapiUrlParams.spec.d.ts +1 -0
- package/dist/types/src/feature/error-messages/emptyForAttribute.d.ts +1 -0
- package/dist/types/src/feature/error-messages/missingInputToMatchForAttribute.d.ts +1 -0
- package/dist/types/src/feature/internal/captureAttributeFromClosestElementWithType.d.ts +1 -0
- package/dist/types/src/index.d.ts +2 -0
- package/dist/types/src/unit/OpenapiSpecRequest.d.ts +691 -0
- package/dist/types/src/unit/OpenapiSpecSession.d.ts +571 -0
- package/dist/types/src/unit/SpecSession.d.ts +2 -18
- package/dist/types/src/unit/helpers/fillOpenapiParams.d.ts +1 -0
- package/dist/types/src/unit/helpers/openapiTypeHelpers.d.ts +29 -0
- package/dist/types/src/unit/helpers/typeHelpers.d.ts +1 -0
- package/dist/types/test-app/src/app/controllers/UserController.d.ts +8 -0
- package/dist/types/test-app/src/app/controllers/UsersController.d.ts +9 -0
- package/package.json +4 -4
- package/src/feature/error-messages/emptyForAttribute.ts +12 -0
- package/src/feature/error-messages/missingInputToMatchForAttribute.ts +10 -0
- package/src/feature/internal/captureAttributeFromClosestElementWithType.ts +40 -0
- package/src/feature/matchers/toCheck.ts +25 -3
- package/src/feature/matchers/toNotMatchTextContent.ts +1 -1
- package/src/feature/matchers/toUncheck.ts +29 -7
- package/src/index.ts +2 -0
- package/src/unit/OpenapiSpecRequest.ts +1435 -0
- package/src/unit/OpenapiSpecSession.ts +1169 -0
- package/src/unit/SpecSession.ts +2 -25
- package/src/unit/helpers/fillOpenapiParams.ts +8 -0
- package/src/unit/helpers/openapiTypeHelpers.ts +79 -0
- package/src/unit/helpers/typeHelpers.ts +1 -0
|
@@ -11,5 +11,17 @@ describe('check', () => {
|
|
|
11
11
|
await check('not found checkbox', { timeout: 500 });
|
|
12
12
|
}).rejects.toThrow();
|
|
13
13
|
});
|
|
14
|
+
it('fails when the label points to an invalid input', async () => {
|
|
15
|
+
await check('My checkbox', { timeout: 500 });
|
|
16
|
+
await expect(async () => {
|
|
17
|
+
await check('My invalid label pointer checkbox', { timeout: 500 });
|
|
18
|
+
}).rejects.toThrow();
|
|
19
|
+
});
|
|
20
|
+
it('fails when the label points to a label with a missing htmlFor statement', async () => {
|
|
21
|
+
await check('My checkbox', { timeout: 500 });
|
|
22
|
+
await expect(async () => {
|
|
23
|
+
await check('My missing htmlFor checkbox', { timeout: 500 });
|
|
24
|
+
}).rejects.toThrow();
|
|
25
|
+
});
|
|
14
26
|
});
|
|
15
27
|
export {};
|
|
@@ -9,6 +9,19 @@ describe('uncheck', () => {
|
|
|
9
9
|
inputValue = await page.$eval('#my-checkbox', input => input.checked).catch(() => null);
|
|
10
10
|
expect(inputValue).toBe(false);
|
|
11
11
|
});
|
|
12
|
+
it('succeeds when label has nested elements with text matching', async () => {
|
|
13
|
+
await check('My other checkbox');
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
15
|
+
let inputValue = await page
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
|
|
17
|
+
.$eval('#my-other-checkbox', input => input.checked)
|
|
18
|
+
.catch(() => null);
|
|
19
|
+
expect(inputValue).toBe(true);
|
|
20
|
+
await uncheck('My other checkbox');
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
|
|
22
|
+
inputValue = await page.$eval('#my-other-checkbox', input => input.checked).catch(() => null);
|
|
23
|
+
expect(inputValue).toBe(false);
|
|
24
|
+
});
|
|
12
25
|
it('fails when the selector is not found', async () => {
|
|
13
26
|
await check('My checkbox');
|
|
14
27
|
await uncheck('My checkbox', { timeout: 500 });
|
|
@@ -16,5 +29,15 @@ describe('uncheck', () => {
|
|
|
16
29
|
await uncheck('not found checkbox', { timeout: 500 });
|
|
17
30
|
}).rejects.toThrow();
|
|
18
31
|
});
|
|
32
|
+
it('fails when the selector is found, but it is not already checked', async () => {
|
|
33
|
+
await check('My checkbox');
|
|
34
|
+
await uncheck('My checkbox', { timeout: 500 });
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
|
|
36
|
+
const inputValue = await page.$eval('#my-checkbox', input => input.checked).catch(() => null);
|
|
37
|
+
expect(inputValue).toBe(false);
|
|
38
|
+
await expect(async () => {
|
|
39
|
+
await uncheck('My checkbox', { timeout: 500 });
|
|
40
|
+
}).rejects.toThrow();
|
|
41
|
+
});
|
|
19
42
|
});
|
|
20
43
|
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { PsychicServer } from '@rvoh/psychic';
|
|
2
|
+
import { OpenapiSpecRequest } from '../../../src/unit/OpenapiSpecRequest.js';
|
|
3
|
+
import User from '../../../test-app/src/app/models/User.js';
|
|
4
|
+
const request = new OpenapiSpecRequest();
|
|
5
|
+
describe('OpenapiSpecRequest#delete', () => {
|
|
6
|
+
beforeEach(async () => {
|
|
7
|
+
await request.init(PsychicServer);
|
|
8
|
+
});
|
|
9
|
+
it('issues a delete request to a controller endpoint, passing when the status matches', async () => {
|
|
10
|
+
const user = await User.create({ email: 'abc@def' });
|
|
11
|
+
await request.delete('/users/{id}', 204, {
|
|
12
|
+
id: user.id,
|
|
13
|
+
headers: {
|
|
14
|
+
hello: 'world',
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
expect(await User.count()).toEqual(0);
|
|
18
|
+
});
|
|
19
|
+
context('without params', () => {
|
|
20
|
+
it('issues a delete request to a controller endpoint, passing when the status matches', async () => {
|
|
21
|
+
await request.delete('/user', 204, {
|
|
22
|
+
headers: {
|
|
23
|
+
hello: 'world',
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
await request.delete('/user', 204);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { PsychicServer } from '@rvoh/psychic';
|
|
2
|
+
import { OpenapiSpecRequest } from '../../../src/unit/OpenapiSpecRequest.js';
|
|
3
|
+
import User from '../../../test-app/src/app/models/User.js';
|
|
4
|
+
const request = new OpenapiSpecRequest();
|
|
5
|
+
describe('OpenapiSpecRequest#get', () => {
|
|
6
|
+
beforeEach(async () => {
|
|
7
|
+
await request.init(PsychicServer);
|
|
8
|
+
});
|
|
9
|
+
it('issues a get request to a controller endpoint, passing when the status matches', async () => {
|
|
10
|
+
const user = await User.create({ email: 'abc@def' });
|
|
11
|
+
const res = await request.get('/users', 200, { headers: { hello: 'world' } });
|
|
12
|
+
expect(res.body).toEqual([{ id: user.id, email: 'abc@def' }]);
|
|
13
|
+
});
|
|
14
|
+
context('with params', () => {
|
|
15
|
+
it('succeeds', async () => {
|
|
16
|
+
const user = await User.create({ email: 'abc@def' });
|
|
17
|
+
const res = await request.get('/users/{id}', 200, {
|
|
18
|
+
id: user.id,
|
|
19
|
+
headers: { hello: 'world' },
|
|
20
|
+
});
|
|
21
|
+
expect(res.body).toEqual({ id: user.id, email: 'abc@def' });
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
context('query params', () => {
|
|
25
|
+
it('issues a get request to a controller endpoint, passing when the status matches', async () => {
|
|
26
|
+
await User.create({ email: 'other@user' });
|
|
27
|
+
const user = await User.create({ email: 'abc@def' });
|
|
28
|
+
const res = await request.get('/users', 200, {
|
|
29
|
+
query: { search: 'abc' },
|
|
30
|
+
headers: { hello: 'world' },
|
|
31
|
+
});
|
|
32
|
+
expect(res.body).toEqual([{ id: user.id, email: 'abc@def' }]);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { PsychicServer } from '@rvoh/psychic';
|
|
2
|
+
import { OpenapiSpecRequest } from '../../../src/unit/OpenapiSpecRequest.js';
|
|
3
|
+
import User from '../../../test-app/src/app/models/User.js';
|
|
4
|
+
const request = new OpenapiSpecRequest();
|
|
5
|
+
describe('OpenapiSpecRequest#patch', () => {
|
|
6
|
+
beforeEach(async () => {
|
|
7
|
+
await request.init(PsychicServer);
|
|
8
|
+
});
|
|
9
|
+
it('issues a patch request to a controller endpoint, passing when the status matches', async () => {
|
|
10
|
+
const user = await User.create({ email: 'abc@def' });
|
|
11
|
+
await request.patch('/users/{id}', 204, {
|
|
12
|
+
id: user.id,
|
|
13
|
+
data: {
|
|
14
|
+
email: 'how@yadoin',
|
|
15
|
+
},
|
|
16
|
+
headers: {
|
|
17
|
+
hello: 'world',
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
await User.findOrFailBy({ email: 'how@yadoin' });
|
|
21
|
+
});
|
|
22
|
+
context('without params', () => {
|
|
23
|
+
it('issues a patch request to a controller endpoint, passing when the status matches', async () => {
|
|
24
|
+
await request.patch('/user', 204, {
|
|
25
|
+
headers: {
|
|
26
|
+
hello: 'world',
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
await request.patch('/user', 204);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { PsychicServer } from '@rvoh/psychic';
|
|
2
|
+
import { OpenapiSpecRequest } from '../../../src/unit/OpenapiSpecRequest.js';
|
|
3
|
+
import User from '../../../test-app/src/app/models/User.js';
|
|
4
|
+
const request = new OpenapiSpecRequest();
|
|
5
|
+
describe('OpenapiSpecRequest#post', () => {
|
|
6
|
+
beforeEach(async () => {
|
|
7
|
+
await request.init(PsychicServer);
|
|
8
|
+
});
|
|
9
|
+
it('issues a get request to a controller endpoint, passing when the status matches', async () => {
|
|
10
|
+
await request.post('/users', 201, {
|
|
11
|
+
data: {
|
|
12
|
+
email: 'how@yadoin',
|
|
13
|
+
},
|
|
14
|
+
headers: {
|
|
15
|
+
hello: 'world',
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
await User.findOrFailBy({ email: 'how@yadoin' });
|
|
19
|
+
});
|
|
20
|
+
context('without params', () => {
|
|
21
|
+
it('issues a post request to a controller endpoint, passing when the status matches', async () => {
|
|
22
|
+
await request.post('/user', 201, {
|
|
23
|
+
headers: {
|
|
24
|
+
hello: 'world',
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
await request.post('/user', 201);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { PsychicServer } from '@rvoh/psychic';
|
|
2
|
+
import { OpenapiSpecRequest } from '../../../src/unit/OpenapiSpecRequest.js';
|
|
3
|
+
import User from '../../../test-app/src/app/models/User.js';
|
|
4
|
+
const request = new OpenapiSpecRequest();
|
|
5
|
+
describe('OpenapiSpecRequest#put', () => {
|
|
6
|
+
beforeEach(async () => {
|
|
7
|
+
await request.init(PsychicServer);
|
|
8
|
+
});
|
|
9
|
+
it('issues a patch request to a controller endpoint, passing when the status matches', async () => {
|
|
10
|
+
const user = await User.create({ email: 'abc@def' });
|
|
11
|
+
await request.put('/users/{id}/update-put', 204, {
|
|
12
|
+
id: user.id,
|
|
13
|
+
data: {
|
|
14
|
+
email: 'how@yadoin',
|
|
15
|
+
},
|
|
16
|
+
headers: {
|
|
17
|
+
hello: 'world',
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
await User.findOrFailBy({ email: 'how@yadoin' });
|
|
21
|
+
});
|
|
22
|
+
context('without params', () => {
|
|
23
|
+
it('issues a put request to a controller endpoint, passing when the status matches', async () => {
|
|
24
|
+
await request.put('/user/update-put', 204, {
|
|
25
|
+
headers: {
|
|
26
|
+
hello: 'world',
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
await request.put('/user/update-put', 204);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { PsychicServer } from '@rvoh/psychic';
|
|
2
|
+
import { OpenapiSpecRequest } from '../../../src/unit/OpenapiSpecRequest.js';
|
|
3
|
+
import User from '../../../test-app/src/app/models/User.js';
|
|
4
|
+
const request = new OpenapiSpecRequest();
|
|
5
|
+
describe('OpenapiSpecSession#delete', () => {
|
|
6
|
+
beforeEach(async () => {
|
|
7
|
+
await request.init(PsychicServer);
|
|
8
|
+
});
|
|
9
|
+
it('issues a delete request to a controller endpoint, passing when the status matches', async () => {
|
|
10
|
+
const user = await User.create({ email: 'abc@def' });
|
|
11
|
+
const session = await request.session('get', '/users', 200);
|
|
12
|
+
await session.delete('/users/{id}', 204, {
|
|
13
|
+
id: user.id,
|
|
14
|
+
});
|
|
15
|
+
expect(await User.count()).toEqual(0);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { PsychicServer } from '@rvoh/psychic';
|
|
2
|
+
import { OpenapiSpecRequest } from '../../../src/unit/OpenapiSpecRequest.js';
|
|
3
|
+
import User from '../../../test-app/src/app/models/User.js';
|
|
4
|
+
const request = new OpenapiSpecRequest();
|
|
5
|
+
describe('OpenapiSpecSession#get', () => {
|
|
6
|
+
beforeEach(async () => {
|
|
7
|
+
await request.init(PsychicServer);
|
|
8
|
+
});
|
|
9
|
+
it('issues a get request to a controller endpoint, passing when the status matches', async () => {
|
|
10
|
+
const user = await User.create({ email: 'abc@def' });
|
|
11
|
+
const session = await request.session('get', '/users', 200);
|
|
12
|
+
const res = await session.get('/users', 200);
|
|
13
|
+
expect(res.body).toEqual([{ id: user.id, email: 'abc@def' }]);
|
|
14
|
+
});
|
|
15
|
+
context('query params', () => {
|
|
16
|
+
it('issues a get request to a controller endpoint, passing when the status matches', async () => {
|
|
17
|
+
await User.create({ email: 'other@user' });
|
|
18
|
+
const user = await User.create({ email: 'abc@def' });
|
|
19
|
+
const session = await request.session('get', '/users', 200);
|
|
20
|
+
const res = await session.get('/users', 200, { query: { search: 'abc' } });
|
|
21
|
+
expect(res.body).toEqual([{ id: user.id, email: 'abc@def' }]);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { PsychicServer } from '@rvoh/psychic';
|
|
2
|
+
import { OpenapiSpecRequest } from '../../../src/unit/OpenapiSpecRequest.js';
|
|
3
|
+
import User from '../../../test-app/src/app/models/User.js';
|
|
4
|
+
const request = new OpenapiSpecRequest();
|
|
5
|
+
describe('OpenapiSpecSession#put', () => {
|
|
6
|
+
beforeEach(async () => {
|
|
7
|
+
await request.init(PsychicServer);
|
|
8
|
+
});
|
|
9
|
+
it('issues a patch request to a controller endpoint, passing when the status matches', async () => {
|
|
10
|
+
const user = await User.create({ email: 'abc@def' });
|
|
11
|
+
const session = await request.session('get', '/users', 200);
|
|
12
|
+
await session.patch('/users/{id}', 204, {
|
|
13
|
+
id: user.id,
|
|
14
|
+
data: {
|
|
15
|
+
email: 'how@yadoin',
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
await User.findOrFailBy({ email: 'how@yadoin' });
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { PsychicServer } from '@rvoh/psychic';
|
|
2
|
+
import { OpenapiSpecRequest } from '../../../src/unit/OpenapiSpecRequest.js';
|
|
3
|
+
import User from '../../../test-app/src/app/models/User.js';
|
|
4
|
+
const request = new OpenapiSpecRequest();
|
|
5
|
+
describe('OpenapiSpecSession#post', () => {
|
|
6
|
+
beforeEach(async () => {
|
|
7
|
+
await request.init(PsychicServer);
|
|
8
|
+
});
|
|
9
|
+
it('issues a get request to a controller endpoint, passing when the status matches', async () => {
|
|
10
|
+
const session = await request.session('get', '/users', 200);
|
|
11
|
+
await session.post('/users', 201, {
|
|
12
|
+
data: {
|
|
13
|
+
email: 'how@yadoin',
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
await User.findOrFailBy({ email: 'how@yadoin' });
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { PsychicServer } from '@rvoh/psychic';
|
|
2
|
+
import { OpenapiSpecRequest } from '../../../src/unit/OpenapiSpecRequest.js';
|
|
3
|
+
import User from '../../../test-app/src/app/models/User.js';
|
|
4
|
+
const request = new OpenapiSpecRequest();
|
|
5
|
+
describe('OpenapiSpecSession#put', () => {
|
|
6
|
+
beforeEach(async () => {
|
|
7
|
+
await request.init(PsychicServer);
|
|
8
|
+
});
|
|
9
|
+
it('issues a patch request to a controller endpoint, passing when the status matches', async () => {
|
|
10
|
+
const user = await User.create({ email: 'abc@def' });
|
|
11
|
+
const session = await request.session('get', '/users', 200);
|
|
12
|
+
await session.put('/users/{id}/update-put', 204, {
|
|
13
|
+
id: user.id,
|
|
14
|
+
data: {
|
|
15
|
+
email: 'how@yadoin',
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
await User.findOrFailBy({ email: 'how@yadoin' });
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import fillOpenapiParams from '../../../../src/unit/helpers/fillOpenapiParams.js';
|
|
2
|
+
describe('fillOpenapiUrlParams', () => {
|
|
3
|
+
it('pushes params into a route', () => {
|
|
4
|
+
expect(fillOpenapiParams('/users/{id}/pets/{petId}/posts/{postId}', {
|
|
5
|
+
id: 1,
|
|
6
|
+
petId: 2,
|
|
7
|
+
postId: 3,
|
|
8
|
+
otherId: 4,
|
|
9
|
+
})).toEqual('/users/1/pets/2/posts/3');
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export default function emptyForAttribute() {
|
|
2
|
+
return `
|
|
3
|
+
When using our uncheck helpers, we expect you to have the correct semantic layout for a checkbox on your page.
|
|
4
|
+
this includes the following:
|
|
5
|
+
|
|
6
|
+
1.) a label element with matching text, with a "for" attribute set (or htmlFor, when using react)
|
|
7
|
+
2.) an input with type="checkbox" which has an id that matches the corresponding for tag on the label
|
|
8
|
+
|
|
9
|
+
We were able to find a label matching the text you gave us, but the for attribute found on it
|
|
10
|
+
was blank. Make sure to add a for attribute, and point it to the id of your corresponding checkbox
|
|
11
|
+
`;
|
|
12
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export default function missingForAttribute(forAttributeValue) {
|
|
2
|
+
return `
|
|
3
|
+
Expected to find an input element matching the "for" attribute on your label, but we could not locate that input element.
|
|
4
|
+
|
|
5
|
+
for attribute found: "${forAttributeValue}"
|
|
6
|
+
|
|
7
|
+
Make sure you have an input element with an id matching "${forAttributeValue}", otherwise this label
|
|
8
|
+
will not be able to be properly associated with the corresponding input.
|
|
9
|
+
`;
|
|
10
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export default async function captureAttributeFromClosestElementWithType(cssSelector, type, attribute) {
|
|
2
|
+
const el = await page.waitForSelector(cssSelector, { timeout: 3000 });
|
|
3
|
+
const attributeValue = (await el.evaluate((element, type, attribute) => {
|
|
4
|
+
// Traverse up the DOM tree to find the closest parent label element
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
6
|
+
let currentElement = element;
|
|
7
|
+
let attrVal = null;
|
|
8
|
+
let millisDrift = 0;
|
|
9
|
+
const startMillis = Date.now();
|
|
10
|
+
while (!attrVal && currentElement && millisDrift < 3000) {
|
|
11
|
+
const currMillis = Date.now();
|
|
12
|
+
millisDrift = currMillis - startMillis;
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
14
|
+
if (currentElement.tagName === type.toUpperCase()) {
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
16
|
+
attrVal = Array.from(currentElement.attributes).find(attr => attr.name === attribute)?.['value'];
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
|
|
20
|
+
currentElement = currentElement.parentElement;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return attrVal;
|
|
24
|
+
}, type, attribute));
|
|
25
|
+
return attributeValue;
|
|
26
|
+
}
|
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
import applyDefaultWaitForOpts from '../helpers/applyDefaultWaitForOpts.js';
|
|
2
|
+
import emptyForAttribute from '../error-messages/emptyForAttribute.js';
|
|
3
|
+
import missingInputToMatchForAttribute from '../error-messages/missingInputToMatchForAttribute.js';
|
|
4
|
+
import captureAttributeFromClosestElementWithType from '../internal/captureAttributeFromClosestElementWithType.js';
|
|
2
5
|
export default async function toCheck(page, expectedText, opts) {
|
|
3
6
|
const failure = {
|
|
4
7
|
pass: false,
|
|
5
8
|
message: () => `Expected page to have checkable element with text: "${expectedText}"`,
|
|
6
9
|
};
|
|
10
|
+
const labelSelector = `label::-p-text("${expectedText}")`;
|
|
7
11
|
try {
|
|
8
|
-
await
|
|
12
|
+
const forAttributeValue = await captureAttributeFromClosestElementWithType(labelSelector, 'label', 'for');
|
|
13
|
+
if (!forAttributeValue) {
|
|
14
|
+
return {
|
|
15
|
+
pass: false,
|
|
16
|
+
message: () => emptyForAttribute(),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
await page.waitForSelector(`#${forAttributeValue}`, applyDefaultWaitForOpts(opts));
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return {
|
|
24
|
+
pass: false,
|
|
25
|
+
message: () => missingInputToMatchForAttribute(forAttributeValue),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
await expect(page).toClickSelector(labelSelector, applyDefaultWaitForOpts(opts));
|
|
9
29
|
return {
|
|
10
30
|
pass: true,
|
|
11
31
|
message: () => {
|
|
@@ -13,7 +13,7 @@ export default async function toNotMatchTextContent(argumentPassedToExpect, expe
|
|
|
13
13
|
successText: () => {
|
|
14
14
|
throw new Error('Cannot negate toNotMatchTextContent, use toMatchTextContent instead');
|
|
15
15
|
},
|
|
16
|
-
failureText: r => `Expected ${r} to match text ${expected}`,
|
|
16
|
+
failureText: r => `Expected ${r} to not match text ${expected}, but it did`,
|
|
17
17
|
timeout: opts.timeout,
|
|
18
18
|
});
|
|
19
19
|
}
|
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
import applyDefaultWaitForOpts from '../helpers/applyDefaultWaitForOpts.js';
|
|
2
|
+
import emptyForAttribute from '../error-messages/emptyForAttribute.js';
|
|
3
|
+
import missingInputToMatchForAttribute from '../error-messages/missingInputToMatchForAttribute.js';
|
|
4
|
+
import captureAttributeFromClosestElementWithType from '../internal/captureAttributeFromClosestElementWithType.js';
|
|
2
5
|
export default async function toUncheck(page, expectedText, opts) {
|
|
3
6
|
try {
|
|
4
7
|
const labelSelector = `label::-p-text("${expectedText}")`;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
const forAttributeValue = await captureAttributeFromClosestElementWithType(labelSelector, 'label', 'for');
|
|
9
|
+
if (!forAttributeValue) {
|
|
10
|
+
return {
|
|
11
|
+
pass: false,
|
|
12
|
+
message: () => emptyForAttribute(),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16
|
+
let inputElement = null;
|
|
17
|
+
try {
|
|
18
|
+
inputElement = await page.waitForSelector(`#${forAttributeValue}`, applyDefaultWaitForOpts(opts));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return {
|
|
22
|
+
pass: false,
|
|
23
|
+
message: () => missingInputToMatchForAttribute(forAttributeValue),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
8
26
|
// eslint-disable-next-line
|
|
9
27
|
const isChecked = await page.evaluate(checkbox => checkbox.checked, inputElement);
|
|
10
28
|
if (!isChecked)
|
package/dist/esm/src/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// unit spec helpers
|
|
2
2
|
export { default as specRequest } from './unit/SpecRequest.js';
|
|
3
3
|
export { default as createPsychicServer } from './unit/createPsychicServer.js';
|
|
4
|
+
export { OpenapiSpecRequest } from './unit/OpenapiSpecRequest.js';
|
|
5
|
+
export { OpenapiSpecSession } from './unit/OpenapiSpecSession.js';
|
|
4
6
|
export { SpecRequest } from './unit/SpecRequest.js';
|
|
5
7
|
export { SpecSession } from './unit/SpecSession.js';
|
|
6
8
|
// feature spec helpers
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import supertest from 'supertest';
|
|
2
|
+
import { createPsychicServer } from '../index.js';
|
|
3
|
+
import fillOpenapiParams from './helpers/fillOpenapiParams.js';
|
|
4
|
+
import { OpenapiSpecSession } from './OpenapiSpecSession.js';
|
|
5
|
+
import supersession from './supersession.js';
|
|
6
|
+
export class OpenapiSpecRequest {
|
|
7
|
+
// eslint-disable-next-line
|
|
8
|
+
PsychicServer;
|
|
9
|
+
// eslint-disable-next-line
|
|
10
|
+
server;
|
|
11
|
+
// final
|
|
12
|
+
async get(uri, expectedStatus, opts) {
|
|
13
|
+
return (await this.makeRequest('get', fillOpenapiParams(uri, (opts || {})), expectedStatus, opts));
|
|
14
|
+
}
|
|
15
|
+
// final
|
|
16
|
+
async post(uri, expectedStatus, opts = {}) {
|
|
17
|
+
return await this.makeRequest('post', uri, expectedStatus, opts);
|
|
18
|
+
}
|
|
19
|
+
// final
|
|
20
|
+
async put(uri, expectedStatus, opts = {}) {
|
|
21
|
+
return await this.makeRequest('put', fillOpenapiParams(uri, opts || {}), expectedStatus, opts);
|
|
22
|
+
}
|
|
23
|
+
// final
|
|
24
|
+
async patch(uri, expectedStatus, opts = {}) {
|
|
25
|
+
return await this.makeRequest('patch', fillOpenapiParams(uri, opts), expectedStatus, opts);
|
|
26
|
+
}
|
|
27
|
+
// final
|
|
28
|
+
async delete(uri, expectedStatus, opts = {}) {
|
|
29
|
+
return await this.makeRequest('delete', fillOpenapiParams(uri, opts), expectedStatus, opts);
|
|
30
|
+
}
|
|
31
|
+
// eslint-disable-next-line
|
|
32
|
+
async init(PsychicServer) {
|
|
33
|
+
// eslint-disable-next-line
|
|
34
|
+
this.PsychicServer = PsychicServer;
|
|
35
|
+
this.server ||= await createPsychicServer(PsychicServer);
|
|
36
|
+
}
|
|
37
|
+
// final
|
|
38
|
+
async session(httpMethod, uri, expectedStatus, opts = {}) {
|
|
39
|
+
const postOpts = opts;
|
|
40
|
+
const getOpts = opts;
|
|
41
|
+
uri = fillOpenapiParams(uri, (opts || {}));
|
|
42
|
+
return await new Promise((accept, reject) => {
|
|
43
|
+
createPsychicServer(this.PsychicServer)
|
|
44
|
+
.then(server => {
|
|
45
|
+
const session = supersession(server);
|
|
46
|
+
// supersession is borrowed from a non-typescript repo, which
|
|
47
|
+
// does not have strong types around http methods, so we need to any cast
|
|
48
|
+
let req = session[(httpMethod || 'post')](`/${uri.replace(/^\//, '')}`);
|
|
49
|
+
if (postOpts.data) {
|
|
50
|
+
req = req.send(postOpts.data);
|
|
51
|
+
}
|
|
52
|
+
req
|
|
53
|
+
.expect(expectedStatus)
|
|
54
|
+
.query(getOpts.query || {})
|
|
55
|
+
.set(postOpts.headers || {})
|
|
56
|
+
.end((err) => {
|
|
57
|
+
if (err)
|
|
58
|
+
return reject(err);
|
|
59
|
+
return accept(new OpenapiSpecSession(session));
|
|
60
|
+
});
|
|
61
|
+
})
|
|
62
|
+
.catch(err => {
|
|
63
|
+
throw err;
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async makeRequest(method, uri, expectedStatus, opts = {}) {
|
|
68
|
+
// TODO: find out why this is necessary. Currently, without initializing the server
|
|
69
|
+
// at the beginning of the specs, supertest is unable to use our server to handle requests.
|
|
70
|
+
// it gives the appearance of being an issue with a runaway promise (i.e. missing await)
|
|
71
|
+
// but I can't find it anywhere, so I am putting this init method in as a temporary fix.
|
|
72
|
+
if (!this.server)
|
|
73
|
+
throw new Error(`
|
|
74
|
+
ERROR:
|
|
75
|
+
When making use of the send spec helper, you must first call "await specRequest.init(PsychicServer)"
|
|
76
|
+
from a beforEach hook at the root of your specs.
|
|
77
|
+
`);
|
|
78
|
+
if (expectedStatus === 500) {
|
|
79
|
+
process.env.PSYCHIC_EXPECTING_INTERNAL_SERVER_ERROR = '1';
|
|
80
|
+
}
|
|
81
|
+
// eslint-disable-next-line
|
|
82
|
+
const req = supertest.agent(this.server.expressApp);
|
|
83
|
+
let request = req[method](`/${uri.replace(/^\//, '')}`);
|
|
84
|
+
if (opts.headers)
|
|
85
|
+
request = request.set(opts.headers);
|
|
86
|
+
if (opts.query)
|
|
87
|
+
request = request.query(opts.query);
|
|
88
|
+
if (method !== 'get')
|
|
89
|
+
request = request.send(opts.data);
|
|
90
|
+
try {
|
|
91
|
+
const res = await request.expect(expectedStatus);
|
|
92
|
+
process.env.PSYCHIC_EXPECTING_INTERNAL_SERVER_ERROR = undefined;
|
|
93
|
+
return res;
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
// without manually console logging, you get no stack trace here
|
|
97
|
+
console.error(err);
|
|
98
|
+
console.trace();
|
|
99
|
+
process.env.PSYCHIC_EXPECTING_INTERNAL_SERVER_ERROR = undefined;
|
|
100
|
+
throw err;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import fillOpenapiParams from './helpers/fillOpenapiParams.js';
|
|
2
|
+
// like SpecRequest, but meant to be bound to an instance
|
|
3
|
+
// of supersession, enabling chained requests to collect cookies
|
|
4
|
+
export class OpenapiSpecSession {
|
|
5
|
+
_session;
|
|
6
|
+
constructor(_session) {
|
|
7
|
+
this._session = _session;
|
|
8
|
+
}
|
|
9
|
+
// final
|
|
10
|
+
async get(uri, expectedStatus, opts) {
|
|
11
|
+
return (await this.makeRequest('get', fillOpenapiParams(uri, (opts || {})), expectedStatus, opts));
|
|
12
|
+
}
|
|
13
|
+
// final
|
|
14
|
+
async post(uri, expectedStatus, opts = {}) {
|
|
15
|
+
return await this.makeRequest('post', uri, expectedStatus, opts);
|
|
16
|
+
}
|
|
17
|
+
// final
|
|
18
|
+
async put(uri, expectedStatus, opts = {}) {
|
|
19
|
+
return await this.makeRequest('put', fillOpenapiParams(uri, opts || {}), expectedStatus, opts);
|
|
20
|
+
}
|
|
21
|
+
// final
|
|
22
|
+
async patch(uri, expectedStatus, opts = {}) {
|
|
23
|
+
return await this.makeRequest('patch', fillOpenapiParams(uri, opts), expectedStatus, opts);
|
|
24
|
+
}
|
|
25
|
+
// final
|
|
26
|
+
async delete(uri, expectedStatus, opts = {}) {
|
|
27
|
+
return await this.makeRequest('delete', fillOpenapiParams(uri, opts), expectedStatus, opts);
|
|
28
|
+
}
|
|
29
|
+
async makeRequest(method, uri, expectedStatus, opts = {}) {
|
|
30
|
+
if (expectedStatus === 500) {
|
|
31
|
+
process.env.PSYCHIC_EXPECTING_INTERNAL_SERVER_ERROR = '1';
|
|
32
|
+
}
|
|
33
|
+
const req = this._session;
|
|
34
|
+
let request = req[method](`/${uri.replace(/^\//, '')}`);
|
|
35
|
+
if (opts.headers)
|
|
36
|
+
request = request.set(opts.headers);
|
|
37
|
+
if (opts.query)
|
|
38
|
+
request = request.query(opts.query);
|
|
39
|
+
if (method !== 'get')
|
|
40
|
+
request = request.send(opts.data);
|
|
41
|
+
try {
|
|
42
|
+
const res = await request.expect(expectedStatus);
|
|
43
|
+
process.env.PSYCHIC_EXPECTING_INTERNAL_SERVER_ERROR = undefined;
|
|
44
|
+
return res;
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
// without manually console logging, you get no stack trace here
|
|
48
|
+
console.error(err);
|
|
49
|
+
console.trace();
|
|
50
|
+
process.env.PSYCHIC_EXPECTING_INTERNAL_SERVER_ERROR = undefined;
|
|
51
|
+
throw err;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|