From d410eedb7c0918ee8fbcf8c094a1df9cae4c8886 Mon Sep 17 00:00:00 2001 From: Paris Holley Date: Mon, 26 Aug 2019 11:54:30 -0400 Subject: [PATCH 1/4] update isObjectWith do support partial checking down the object tree #110 --- .gitignore | 1 + src/Match/MatchObjectWith.ts | 33 +++++++++---- test/spec/Mock.test.ts | 90 ++++++++++++++++++++++++++++++++++++ test/spec/fixtures.ts | 4 ++ 4 files changed, 120 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 5405144..a19326e 100644 --- a/.gitignore +++ b/.gitignore @@ -47,4 +47,5 @@ node_modules/ .tmp/ sauce.json .vscode/ +.idea dist/ diff --git a/src/Match/MatchObjectWith.ts b/src/Match/MatchObjectWith.ts index dade9f3..327afeb 100644 --- a/src/Match/MatchObjectWith.ts +++ b/src/Match/MatchObjectWith.ts @@ -4,21 +4,38 @@ import { Consts } from "../Consts"; import { Utils } from "../Common/Utils"; export class MatchObjectWith implements IMatch { - + readonly ___id = Consts.IMATCH_ID_VALUE; - + private readonly _value: T; constructor(value: T) { this._value = _.cloneDeep(value); } - ___matches(object: Object): boolean { - let match = false; - let partial = _.pick(object, _.keys(this._value)); - if (_.isEqual(this._value, partial)) - match = true; - return match; + ___matches(object: Object, value?: Object): boolean { + const compare = (value || this._value) as any; + const compareKeys = _.keys(compare); + const partial = _.pick(object, compareKeys) as any; + const partialKeys = _.keys(partial); + + if (compareKeys.length !== partialKeys.length) { + return false; + } + + for (const key of partialKeys) { + const nested = partial[key]; + if (_.isArray(nested) || _.isObject(nested)) { + if (!this.___matches(nested, compare[key])) { + return false; + } + } else if (!_.isEqual(nested, compare[key])) { + console.log(nested, '=', compare[key]); + return false; + } + } + + return true; } toString(): string { diff --git a/test/spec/Mock.test.ts b/test/spec/Mock.test.ts index 5d26e28..123d3eb 100644 --- a/test/spec/Mock.test.ts +++ b/test/spec/Mock.test.ts @@ -603,6 +603,96 @@ describe("Mock", () => { expect(mock.object.doString()).to.eq(undefined); }); + it("should match a method with partial nested object value params", () => { + + const bar1nested = new TypeMoqTests.Bar(); + bar1nested.anyValue = 42; + bar1nested.enumValue = TypeMoqTests.AnEnum.One; + const bar1 = new TypeMoqTests.Bar(); + bar1.value = "Lorem ipsum dolor sit amet"; + bar1.nested = bar1nested; + + const bar2nested = new TypeMoqTests.Bar(); + bar2nested.anyValue = 42; + bar2nested.enumValue = TypeMoqTests.AnEnum.Two; + + const bar2 = new TypeMoqTests.Bar(); + bar2.value = "Ut enim ad minim veniam"; + bar2.nested = bar2nested; + const match = {nested: {enumValue: TypeMoqTests.AnEnum.One}}; + const mock = Mock.ofType(TypeMoqTests.Doer); + + mock.setup(x => x.doObject(It.isObjectWith(match))).returns(() => "At vero eos et accusamus et iusto odio dignissimos ducimus"); + + expect(mock.object.doObject(bar1)).to.eq("At vero eos et accusamus et iusto odio dignissimos ducimus"); + expect(mock.object.doObject(bar2)).to.eq(undefined); + + bar2nested.enumValue = TypeMoqTests.AnEnum.One; + expect(mock.object.doObject(bar2)).to.eq("At vero eos et accusamus et iusto odio dignissimos ducimus"); + + expect(mock.object.doObject(new Object())).to.eq(undefined); + expect(mock.object.doObject({ foo: 'nothing' })).to.eq(undefined); + expect(mock.object.doObject()).to.eq(undefined); + }); + + it("should match a method with partial nested object array value params", () => { + + const bar1nested = new TypeMoqTests.Bar(); + bar1nested.anyValue = 42; + bar1nested.enumValue = TypeMoqTests.AnEnum.One; + const bar1 = new TypeMoqTests.Bar(); + bar1.value = "Lorem ipsum dolor sit amet"; + bar1.nesteds = [bar1nested]; + + const bar2nested = new TypeMoqTests.Bar(); + bar2nested.anyValue = 42; + bar2nested.enumValue = TypeMoqTests.AnEnum.Two; + + const bar2 = new TypeMoqTests.Bar(); + bar2.value = "Ut enim ad minim veniam"; + bar2.nesteds = [bar2nested]; + const match = {nesteds: [{enumValue: TypeMoqTests.AnEnum.One}]}; + const mock = Mock.ofType(TypeMoqTests.Doer); + + mock.setup(x => x.doObject(It.isObjectWith(match))).returns(() => "At vero eos et accusamus et iusto odio dignissimos ducimus"); + + expect(mock.object.doObject(bar1)).to.eq("At vero eos et accusamus et iusto odio dignissimos ducimus"); + expect(mock.object.doObject(bar2)).to.eq(undefined); + + bar2nested.enumValue = TypeMoqTests.AnEnum.One; + expect(mock.object.doObject(bar2)).to.eq("At vero eos et accusamus et iusto odio dignissimos ducimus"); + + expect(mock.object.doObject(new Object())).to.eq(undefined); + expect(mock.object.doObject({ foo: 'nothing' })).to.eq(undefined); + expect(mock.object.doObject()).to.eq(undefined); + }); + + it("should match a method with partial array value params", () => { + + const bar1 = new TypeMoqTests.Bar(); + bar1.value = "Lorem ipsum dolor sit amet"; + bar1.anyValue = 42; + bar1.enumValue = TypeMoqTests.AnEnum.One; + const bar2 = new TypeMoqTests.Bar(); + bar2.value = "Ut enim ad minim veniam"; + bar2.enumValue = TypeMoqTests.AnEnum.Two; + const match = [{ anyValue: 42, enumValue: TypeMoqTests.AnEnum.One }]; + const mock = Mock.ofType(TypeMoqTests.Doer); + + mock.setup(x => x.doObject(It.isObjectWith(match))).returns(() => "At vero eos et accusamus et iusto odio dignissimos ducimus"); + + expect(mock.object.doObject([bar1])).to.eq("At vero eos et accusamus et iusto odio dignissimos ducimus"); + expect(mock.object.doObject([bar2])).to.eq(undefined); + + bar2.anyValue = 42; + bar2.enumValue = TypeMoqTests.AnEnum.One; + expect(mock.object.doObject([bar2])).to.eq("At vero eos et accusamus et iusto odio dignissimos ducimus"); + + expect(mock.object.doObject([new Object()])).to.eq(undefined); + expect(mock.object.doObject([{ foo: 'nothing' }])).to.eq(undefined); + expect(mock.object.doObject([])).to.eq(undefined); + }); + it("should match a method with partial object value params", () => { const bar1 = new TypeMoqTests.Bar(); diff --git a/test/spec/fixtures.ts b/test/spec/fixtures.ts index 493016a..8e28b62 100644 --- a/test/spec/fixtures.ts +++ b/test/spec/fixtures.ts @@ -70,12 +70,16 @@ export module TypeMoqTests { value: string = ''; anyValue: any = undefined; enumValue: AnEnum; + nesteds?: Bar[]; + nested?: Bar; } export interface IBar { value: string; anyValue: any; enumValue: AnEnum; + nested?: IBar; + nesteds?: IBar[]; } export interface IDo { From 1b7298939d4887558255283ea150aa05a48ff718 Mon Sep 17 00:00:00 2001 From: Paris Holley Date: Mon, 26 Aug 2019 12:27:45 -0400 Subject: [PATCH 2/4] library doesn't build on latest versions --- .nvmrc | 1 + .travis.yml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +8 diff --git a/.travis.yml b/.travis.yml index 53b2dae..1746481 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,10 @@ language: node_js node_js: - - "node" + - 8 install: - "npm install" script: - - "gulp test:travis" \ No newline at end of file + - "gulp test:travis" From 8803e248a0192d90072ac6ba9e0135bd3d40dfaf Mon Sep 17 00:00:00 2001 From: Paris Holley Date: Mon, 26 Aug 2019 12:41:56 -0400 Subject: [PATCH 3/4] sauce does not appear to be configured on travis anymore --- gulpfile.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 26e2e06..8362832 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -232,8 +232,8 @@ gulp.task('test:mocha.es6', function () { function runMocha(srcPath) { return gulp.src(srcPath) - .pipe($.spawnMocha({ - ui: 'bdd', + .pipe($.spawnMocha({ + ui: 'bdd', reporter: 'spec', env: {'NODE_PATH': './.tmp/src'} })) @@ -289,5 +289,5 @@ gulp.task('release', ['default'], function (cb) { }); gulp.task('test:travis', ['build'], function (cb) { - runSequence('test:sauce', 'test:mocha', 'test:mocha.es6', cb); + runSequence('test:mocha', 'test:mocha.es6', cb); }); From d914a655840b0d0b1ed327fddda9fe78abbdedf4 Mon Sep 17 00:00:00 2001 From: Paris Holley Date: Tue, 27 Aug 2019 14:59:25 -0400 Subject: [PATCH 4/4] date/primitive fix --- src/Match/MatchObjectWith.ts | 11 +++++++---- test/spec/Mock.test.ts | 30 ++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/Match/MatchObjectWith.ts b/src/Match/MatchObjectWith.ts index 327afeb..e6433f9 100644 --- a/src/Match/MatchObjectWith.ts +++ b/src/Match/MatchObjectWith.ts @@ -1,7 +1,7 @@ import * as _ from "lodash"; -import { IMatch } from "./IMatch"; -import { Consts } from "../Consts"; -import { Utils } from "../Common/Utils"; +import {IMatch} from "./IMatch"; +import {Consts} from "../Consts"; +import {Utils} from "../Common/Utils"; export class MatchObjectWith implements IMatch { @@ -23,6 +23,10 @@ export class MatchObjectWith implements IMatch { return false; } + if (!partialKeys.length) { + return _.isEqual(object, value); + } + for (const key of partialKeys) { const nested = partial[key]; if (_.isArray(nested) || _.isObject(nested)) { @@ -30,7 +34,6 @@ export class MatchObjectWith implements IMatch { return false; } } else if (!_.isEqual(nested, compare[key])) { - console.log(nested, '=', compare[key]); return false; } } diff --git a/test/spec/Mock.test.ts b/test/spec/Mock.test.ts index 123d3eb..144f7d7 100644 --- a/test/spec/Mock.test.ts +++ b/test/spec/Mock.test.ts @@ -603,6 +603,36 @@ describe("Mock", () => { expect(mock.object.doString()).to.eq(undefined); }); + it("should not treat date objects as the same when using isObjectWith", () => { + const date1 = new Date(Date.now() - 1000000000); + const date2 = new Date(Date.now() - 1000000001); + + const bar1nested = new TypeMoqTests.Bar(); + bar1nested.anyValue = date1; + const bar1 = new TypeMoqTests.Bar(); + bar1.nested = bar1nested; + + const bar2nested = new TypeMoqTests.Bar(); + bar2nested.anyValue = date2; + + const bar2 = new TypeMoqTests.Bar(); + bar2.nested = bar2nested; + const match = {nested: {anyValue: date1}}; + const mock = Mock.ofType(TypeMoqTests.Doer); + + mock.setup(x => x.doObject(It.isObjectWith(match))).returns(() => "At vero eos et accusamus et iusto odio dignissimos ducimus"); + + expect(mock.object.doObject(bar1)).to.eq("At vero eos et accusamus et iusto odio dignissimos ducimus"); + expect(mock.object.doObject(bar2)).to.eq(undefined); + + bar2nested.anyValue = date1; + expect(mock.object.doObject(bar2)).to.eq("At vero eos et accusamus et iusto odio dignissimos ducimus"); + + expect(mock.object.doObject(new Object())).to.eq(undefined); + expect(mock.object.doObject({ foo: 'nothing' })).to.eq(undefined); + expect(mock.object.doObject()).to.eq(undefined); + }); + it("should match a method with partial nested object value params", () => { const bar1nested = new TypeMoqTests.Bar();