Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow match() to be used for any mock request #452

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 0 additions & 110 deletions addon/mocks/attribute-matcher.js

This file was deleted.

29 changes: 24 additions & 5 deletions addon/mocks/mock-create-request.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
import { assert } from '@ember/debug';
import { isPresent } from '@ember/utils';
import FactoryGuy from '../factory-guy';
import MockStoreRequest from './mock-store-request';
import AttributeMatcher from './attribute-matcher';
import { assign } from '@ember/polyfills';

export default class MockCreateRequest extends AttributeMatcher(
MockStoreRequest
) {
export default class MockCreateRequest extends MockStoreRequest {
constructor(modelName, { model } = {}) {
super(modelName, 'createRecord');
this.model = model;
this.returnArgs = {};
this.matchArgs = {};
this.setupHandler();
}

Expand All @@ -32,6 +29,28 @@ export default class MockCreateRequest extends AttributeMatcher(
return this;
}

/**
Update and Create mocks can accept 2 return keys 'attrs' and 'add'

@param options
@returns {Array}
*/
validateReturnsOptions(options) {
const responseKeys = Object.keys(options),
validReturnsKeys = ['attrs', 'add'],
invalidKeys = responseKeys.filter(
(key) => !validReturnsKeys.includes(key)
);

assert(
`[ember-data-factory-guy] You passed invalid keys for 'returns' function.
Valid keys are ${validReturnsKeys}. You used these invalid keys: ${invalidKeys}`,
invalidKeys.length === 0
);

return responseKeys;
}

/**
Unless the id is setup already in the return args, then setup a new id.
*/
Expand Down
24 changes: 24 additions & 0 deletions addon/mocks/mock-request.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { assert } from '@ember/debug';
import { typeOf } from '@ember/utils';
import { assign } from '@ember/polyfills';
import {
isEmptyObject,
Expand Down Expand Up @@ -156,7 +157,25 @@ export default class {
return true;
}

attributesMatch(requestBody, matchParams) {
if (isEmptyObject(matchParams)) {
return true;
}
return isPartOf(toParams(requestBody), toParams(matchParams));
}

extraRequestMatches(request) {
if (this.matchArgs) {
let requestBody = request.requestBody;
if (typeOf(requestBody) === 'string') {
requestBody = JSON.parse(requestBody);
}
if (typeOf(this.matchArgs) === 'function') {
return this.matchArgs(requestBody);
} else {
return this.attributesMatch(requestBody, this.matchArgs);
}
}
return this.paramsMatch(request);
}

Expand All @@ -179,6 +198,11 @@ export default class {
return true;
}

match(matches) {
this.matchArgs = matches;
return this;
}

// mockId holds the url for this mock request
oldUrl() {
return this.mockId && this.mockId.url;
Expand Down
46 changes: 46 additions & 0 deletions addon/mocks/mock-store-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/* eslint-disable ember/no-get, ember/classic-decorator-no-classic-methods */
import FactoryGuy from '../factory-guy';
import MockRequest from './mock-request';
import { isEmptyObject, isEquivalent } from '../utils/helper-functions';

export default class extends MockRequest {
constructor(modelName, requestType) {
Expand Down Expand Up @@ -54,4 +55,49 @@ export default class extends MockRequest {

return { adapterOptions: this.adapterOptions, record };
}

/**
This is tricky, but the main idea here is:

#1 Take the keys they want to match and transform them to what the serialized
version would be ie. company => company_id

#2 Take the matchArgs and turn them into a FactoryGuy payload class by
FactoryGuy.build(ing) them into a payload

#3 Wrap the request data into a FactoryGuy payload class

#4 Go though the keys from #1 and check that both the payloads from #2/#3 have the
same values

@param requestData
@returns {boolean} true is no attributes to match or they all match
*/
attributesMatch(requestData, matchParams) {
if (isEmptyObject(matchParams)) {
return true;
}

let builder = FactoryGuy.fixtureBuilder(this.modelName);

// transform they match keys
let matchCheckKeys = Object.keys(matchParams).map((key) => {
return builder.transformKey(this.modelName, key);
});
// build the match args into a JSONPayload class
let buildOpts = { serializeMode: true, transformKeys: true };
let expectedData = builder.convertForBuild(
this.modelName,
matchParams,
buildOpts
);

// wrap request data in a JSONPayload class
builder.wrapPayload(this.modelName, requestData);

// success if all values match
return matchCheckKeys.every((key) => {
return isEquivalent(expectedData.get(key), requestData.get(key));
});
}
}
26 changes: 23 additions & 3 deletions addon/mocks/mock-update-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@ import { assert } from '@ember/debug';
import { assign } from '@ember/polyfills';
import FactoryGuy from '../factory-guy';
import MockStoreRequest from './mock-store-request';
import AttributeMatcher from './attribute-matcher';
import MaybeIdUrlMatch from './maybe-id-url-match';

export default class MockUpdateRequest extends MaybeIdUrlMatch(
AttributeMatcher(MockStoreRequest)
MockStoreRequest
) {
constructor(modelName, { id, model } = {}) {
super(modelName, 'updateRecord');
this.id = id;
this.model = model;
this.returnArgs = {};
this.matchArgs = {};
this.setupHandler();
}

Expand Down Expand Up @@ -46,6 +44,28 @@ export default class MockUpdateRequest extends MaybeIdUrlMatch(
return this;
}

/**
Update and Create mocks can accept 2 return keys 'attrs' and 'add'

@param options
@returns {Array}
*/
validateReturnsOptions(options) {
const responseKeys = Object.keys(options),
validReturnsKeys = ['attrs', 'add'],
invalidKeys = responseKeys.filter(
(key) => !validReturnsKeys.includes(key)
);

assert(
`[ember-data-factory-guy] You passed invalid keys for 'returns' function.
Valid keys are ${validReturnsKeys}. You used these invalid keys: ${invalidKeys}`,
invalidKeys.length === 0
);

return responseKeys;
}

/**
Adapters freak out if update payload is non empty and there is no id.

Expand Down
28 changes: 28 additions & 0 deletions tests/unit/mocks/mock-any-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,34 @@ module('MockAny', function (hooks) {
);
});

test('match the request body', async function (assert) {
const method = 'POST',
url = '/api/post-stuff',
whatsUp = { whats: 'up' };

const theMock = mock({ url, type: method }).match(whatsUp);

await fetchJSON({ url, params: whatsUp, method });
assert.equal(theMock.timesCalled, 1, 'exact match');

await fetchJSON({ url, params: { whats: 'up', foo: 'bar' }, method });
assert.equal(theMock.timesCalled, 2, 'partial match');
});

test('match a function', async function (assert) {
assert.expect(1);
const method = 'POST',
url = '/api/post-stuff',
whatsUp = { whats: 'up' };

mock({ url, type: method }).match(function (requestData) {
assert.deepEqual(requestData, whatsUp);
return true;
});

await fetchJSON({ url, params: whatsUp, method });
});

test('mock#timesCalled works as expected', async function (assert) {
const method = 'POST',
url = '/api/post-stuff';
Expand Down