model-layer
Version:
526 lines (443 loc) • 14.2 kB
text/typescript
import {Model, Types} from "../../lib/index";
import assert from "assert";
import {invalidValuesAsString, eol} from "../../lib/utils";
describe("ModelType", () => {
it("child model", () => {
class UserModel extends Model<UserModel> {
structure() {
return {
name: Types.String({
trim: true,
emptyAsNull: true,
required: true
}),
phone: Types.String({
validate: /^\+7 \(\d\d\d\) \d\d\d-\d\d-\d\d$/
}),
email: Types.String({
required: true,
validate: /^[\w.-]+@[\w.-]+\.\w+$/i
}),
age: Types.Number({
required: true,
validate: (age) =>
age > 0
})
};
}
}
class RegistrationModel extends Model<RegistrationModel> {
structure() {
return {
user: Types.Model({
Model: UserModel,
required: true
}),
date: Types.Date({
required: true
})
};
}
}
const AnyRegistrationModel = RegistrationModel as any;
assert.throws(
() => {
const regModel = new RegistrationModel({
date: Date.now()
});
},
(err: Error) =>
err.message === "required user"
);
assert.throws(
() => {
const regModel = new RegistrationModel({
date: Date.now(),
user: null as any
});
},
(err: Error) =>
err.message === "required user"
);
assert.throws(
() => {
const regModel = new AnyRegistrationModel({
date: Date.now(),
user: []
});
},
(err: Error) =>
err.message === "invalid model UserModel for user: []"
);
assert.throws(
() => {
const regModel = new AnyRegistrationModel({
date: Date.now(),
user: false
});
},
(err: Error) =>
err.message === "invalid model UserModel for user: false"
);
assert.throws(
() => {
const regModel = new AnyRegistrationModel({
date: Date.now(),
user: NaN
});
},
(err: Error) =>
err.message === "invalid model UserModel for user: NaN"
);
assert.throws(
() => {
const regModel = new AnyRegistrationModel({
date: Date.now(),
user: /x/
});
},
(err: Error) =>
err.message === "invalid model UserModel for user: /x/"
);
assert.throws(
() => {
const regModel = new RegistrationModel({
date: Date.now(),
user: {
name: "10",
age: 101
}
});
},
(err: Error) =>
err.message === `invalid model UserModel for user: {"name":"10","age":101},${eol} required email`
);
const now = Date.now();
const registrationModel = new RegistrationModel({
date: now,
user: {
name: "Bob ",
age: "99" as any,
email: "x@x.x"
}
});
assert.strictEqual( +registrationModel.row.date!, now );
assert.ok( registrationModel.row.date instanceof Date );
const user = registrationModel.row.user!;
assert.strictEqual( user.row.name, "Bob" );
assert.strictEqual( user.row.age, 99 );
assert.strictEqual( user.row.email, "x@x.x" );
assert.strictEqual( user.row.phone, null );
});
it("model.toJSON with custom models", () => {
class CarModel extends Model<CarModel> {
structure() {
return {
id: Types.Number,
color: Types.String
};
}
}
class UserModel extends Model<UserModel> {
structure() {
return {
name: Types.String,
car: CarModel
};
}
}
const userModel = new UserModel({
name: "Jack",
car: {
id: "1" as any,
color: "red"
}
});
assert.deepEqual(
userModel.toJSON(),
{
name: "Jack",
car: {
id: 1,
color: "red"
}
}
);
});
it("equal models", () => {
class SomeModel extends Model<SomeModel> {
structure() {
return {
prop: Types.Number
};
}
}
const model1 = new SomeModel({
prop: 1
});
const model2 = new SomeModel({
prop: 1
});
const model3 = new SomeModel({
prop: 3
});
const obj1 = {prop: 1};
const obj2 = {prop: 3};
const pairs: any[][] = [
[null, null, true],
[null, model1, false],
[null, model2, false],
[null, model3, false],
[model1, model1, true],
[model1, model2, true],
[model1, model3, false],
[model2, model3, false],
[model1, obj1, true],
[model1, obj2, false],
[model3, obj1, false],
[model3, obj2, true]
];
pairs.forEach((pair) => {
class TestModel extends Model<TestModel> {
structure() {
return {
model: SomeModel
};
}
}
const testModel1 = new TestModel({
model: pair[0]
});
const testModel2 = new TestModel({
model: pair[1]
});
assert.strictEqual(
testModel1.equal( testModel2 ),
pair[2],
pair.toString()
);
assert.strictEqual(
testModel2.equal( testModel1 ),
pair[2],
pair.toString()
);
});
});
it("equal circular models", () => {
class SomeModel extends Model<SomeModel> {
structure() {
return {
name: Types.String,
self: SomeModel
};
}
}
const circular1 = new SomeModel();
circular1.set({self: circular1});
const circular2 = new SomeModel();
circular2.set({self: circular2});
const circular3 = new SomeModel({name: "nice"});
circular3.set({self: circular3});
const pairs: any[][] = [
[circular1, circular1, true],
[circular1, circular2, true],
[circular2, circular2, true],
[circular1, circular3, false],
[circular2, circular3, false]
];
pairs.forEach((pair, i) => {
class TestModel extends Model<TestModel> {
structure() {
return {
model: SomeModel
};
}
}
const model1 = new TestModel({
model: pair[0]
});
const model2 = new TestModel({
model: pair[1]
});
assert.strictEqual(
model1.equal( model2 ),
pair[2],
i + ": " + invalidValuesAsString(pair)
);
assert.strictEqual(
model2.equal( model1 ),
pair[2],
i + ": " + invalidValuesAsString(pair)
);
});
});
// when in type defined BaseModel, and in row we have ChildModel (extends BaseModel),
// then clone should be instance of ChildModel
it("clone model, should return instance of Child", () => {
class FirstLevel<T extends FirstLevel = any> extends Model<FirstLevel & T> {
structure() {
return {
};
}
}
class SecondLevel extends FirstLevel<SecondLevel> {
structure() {
return {
level: Types.Number({
default: 2
})
};
}
}
class MainModel extends Model<MainModel> {
structure() {
return {
some: FirstLevel
};
}
}
const second = new SecondLevel();
const main = new MainModel({
some: second
});
assert.deepEqual(
main.toJSON(),
{
some: {
level: 2
}
}
);
const clone = main.clone();
assert.ok( clone.get("some") instanceof SecondLevel );
});
it("triple extending", () => {
class First<T extends First = any> extends Model<T> {
structure() {
return {
first: Types.Number
};
}
}
class Second<T extends Second = any> extends First<T> {
structure() {
return {
...super.structure(),
second: Types.Number
};
}
}
class Third<T extends Third = any> extends Second<T> {
structure() {
return {
...super.structure(),
third: Types.Number
};
}
}
const first = new First<First>({
first: 1
});
const second = new Second<Second>({
first: 1,
second: 2
});
const third = new Third<Third>({
first: 1,
second: 2,
third: 3
});
assert.deepStrictEqual( first.toJSON(), {
first: 1
});
assert.deepStrictEqual( second.toJSON(), {
first: 1,
second: 2
});
assert.deepStrictEqual( third.toJSON(), {
first: 1,
second: 2,
third: 3
});
assert.strictEqual( first.get("first"), 1 );
assert.strictEqual( second.get("second"), 2 );
assert.strictEqual( third.get("third"), 3 );
});
it("circular structure to json", () => {
class MyModel extends Model<MyModel> {
structure() {
return {
model: MyModel
};
}
}
const model = new MyModel();
model.set({model});
assert.throws(
() => {
model.toJSON();
},
(err: Error) =>
err.message === "Cannot converting circular structure to JSON"
);
});
it("same model as value in two fields", () => {
class User extends Model<User> {
structure() {
return {
name: Types.String
};
}
}
class TestModel extends Model<TestModel> {
structure() {
return {
a: User,
b: User
};
}
}
const user = new User({
name: "Jack"
});
const test = new TestModel({
a: user,
b: user
});
assert.deepStrictEqual(test.toJSON(), {
a: {name: "Jack"},
b: {name: "Jack"}
});
});
it("save parent reference after clone", () => {
class User extends Model<User> {
structure() {
return {
name: Types.String
};
}
}
class TestModel extends Model<TestModel> {
structure() {
return {
a: User,
b: User
};
}
}
const user = new User({
name: "Jack"
});
const test = new TestModel({
a: user,
b: user
});
const testClone = test.clone();
const cloneUserA = testClone.get("a")!;
const cloneUserB = testClone.get("b")!;
const cloneUserAParent = cloneUserA.findParentInstance(TestModel);
const cloneUserBParent = cloneUserB.findParentInstance(TestModel);
assert.ok(cloneUserAParent === testClone, "valid clone parent A");
assert.ok(cloneUserBParent === testClone, "valid clone parent B");
});
});