Mocha tests, clean disk database before every file runs - unit-testing

I am using Sails 1.x.
Is it possible to reset the Sails.js database before each test file runs? I want it to be in state after sails.lift() completes before each run. I followed the docs here - https://sailsjs.com/documentation/concepts/testing - but did not end up with any solution like this.
The only solution I'm having right now is to change the lifecyle.test.js before and after to run beforeEvery and afterEvery - https://sailsjs.com/documentation/concepts/testing - so this is lifting everytime before test. But it takes a long time to lift.

This is very simple to do. You just need to specify to add test connection in your connections on datasourses (depends on the version of Sails.js), setup it as active during the test and provide migration strategy 'drop' which is just rebuild your DB every time on startup
models: {
connection: 'test',
migrate: 'drop'
},
My connections Sails.js 0.12.14
module.exports.connections = {
prod: {
adapter: 'sails-mongo',
host: 'localhost',
port: 27017,
database: 'some-db'
},
test: {
adapter: 'sails-memory'
},
};
My simplified lifecycle.test.js
let app;
// Before running any tests...
before(function(done) {
// Lift Sails and start the server
const Sails = require('sails').constructor;
const sailsApp = new Sails();
sailsApp.lift({
models: {
connection: 'test',
migrate: 'drop'
},
}, function(err, sails) {
app = sails;
return done(err, sails);
});
});
// After all tests have finished...
after(async function() {
// here you can clear fixtures, etc.
// (e.g. you might want to destroy the records you created above)
try {
await app.lower()
} catch (err) {
await app.lower()
}
});
In Sails 1 it's even simpler
const sails = require('sails');
before((done) => {
sails.lift({
datastores: {
default: {
adapter: 'sails-memory'
},
},
hooks: { grunt: false },
models: {
migrate: 'drop'
},
}, (err) => {
if (err) { return done(err); }
return done();
});
});
after(async () => {
await sails.lower();
});

I'm using this in my bootstrap test file to make the database clean.
const sails = require('sails');
before((done) => {
sails.lift({
log: {
level: 'silent'
},
datastores: {
default: {
adapter: 'sails-disk',
inMemoryOnly: true
}
},
models: {
migrate: 'drop',
archiveModelIdentity: false
}
}, function (err, sails) {
if (err) return done(err);
done(err, sails);
});
});
after(async () => {
await sails.lower();
});
beforeEach((done) => {
sails.once('hook:orm:reloaded', done);
sails.emit('hook:orm:reload');
});

Related

What is the best way to mock ember services that use ember-ajax in ember-cli-storybook to post and fetch data?

I'm using Ember CLI Storybook to create a story of a component than internally relies upon services that communicate to the internet, to fetch and post information to the backend. The way I'm doing that is using ember-ajax.
I see how to mock an ember model from this section but wondering if there is a workaround for ember ajax service.
I like to use mswjs.io for mocking remote requests. It uses a service worker so you can still use your network log as if you still used your real API.
I have an example repo here showing how to set it up: https://github.com/NullVoxPopuli/ember-data-resources/
But I'll copy the code, in case I change something.
Now, in tests, you'd want something like this: https://github.com/NullVoxPopuli/ember-data-resources/blob/main/tests/unit/find-record-test.ts#L17
module('findRecord', function (hooks) {
setupMockData(hooks);
But since you're using storybook, you'd instead want the contents of that function. (And without the setup/teardown hooks unique to tests)
https://github.com/NullVoxPopuli/ember-data-resources/blob/main/tests/unit/-mock-data.ts#L22
import { rest, setupWorker } from 'msw';
let worker;
export async function setupMockData() {
if (!worker) {
worker = setupWorker();
await worker.start();
// artificial timeout "just in case" worker takes a bit to boot
await new Promise((resolve) => setTimeout(resolve, 1000));
worker.printHandlers();
}
let data = [
{ id: '1', type: 'blogs', attributes: { name: `name:1` } },
{ id: '2', type: 'blogs', attributes: { name: `name:2` } },
{ id: '3', type: 'blogs', attributes: { name: `name:3` } },
];
worker.use(
rest.get('/blogs', (req, res, ctx) => {
let id = req.url.searchParams.get('q[id]');
if (id) {
let record = data.find((datum) => datum.id === id);
return res(ctx.json({ data: record }));
}
return res(ctx.json({ data }));
}),
rest.get('/blogs/:id', (req, res, ctx) => {
let { id } = req.params;
let record = data.find((datum) => datum.id === id);
if (record) {
return res(ctx.json({ data: record }));
}
return res(
ctx.status(404),
ctx.json({ errors: [{ status: '404', detail: 'Blog not found' }] })
);
})
);
}
Docs for msw: https://mswjs.io/

Vue test utils incorrect value of computed

Hello I have checked the behaviour in application and it works with same data from api as I'm providing in mocked api call Api.contracts.getContractDetails.mockImplementationOnce(() => ({ data })); However, the value of hasWatermark computed is false - while it should be true.
How can I debug this? Is it possible to check computed in tests? This is my test:
function createWrapper() {
const i18n = new VueI18n({
locale: "en",
missing: jest.fn(),
});
return mount(EmployeeContract, {
i18n,
localVue,
store,
mocks: { $route: { query: {}, params: { id: "123" } }, $buefy: { toast: { open: jest.fn() } } },
stubs: ["spinner", "router-link", "b-switch"],
});
it("should add watermark for preview once it has rejected status", async () => {
const data = singleContract;
Api.contracts.getContractDetails.mockImplementationOnce(() => ({ data }));
const wrapper = createWrapper();
await flushPromises();
expect(wrapper.vm.hasWatermark).toBeTruthy();
});

Test is passing, but assertion error given after

I have this weird situation which I don't understand.
In my vue component I use "Repository / Service" for making API calls. It uses axios:
I import it in a component:
import { RepositoryFactory } from "AMComponents/repositories/repository-factory";
const ContactsRepository = RepositoryFactory.get("contacts");
And in method addContact I use it like that:
Now,
This is how I test it:
test("addContact", () => {
const newContact = {
"name": "Hulk Hogan",
"email": "hulk#hogan.com",
"phone": "",
"additional_information": "Hulkamania is running wild, Brother",
"type": "advertiser",
};
ContactsRepository.createContact = jest.fn()
.mockResolvedValue({
data: {
response: {
contact_information: [...contacts, newContact],
passport_privileges: {},
},
},
})
.mockRejectedValue({
response: {
status: 400,
data: {
messages: ["mocked error"],
},
},
});
window.toastr = {
success: jest.fn(),
error: jest.fn(),
};
wrapper.vm.addContact(newContact);
expect(wrapper.vm.saving).toBe(true);
expect(ContactsRepository.createContact).toHaveBeenCalled();
flushPromises()
.then(() => {
expect(1).toBe(2); // <-- here is where I expect my test to fail
expect(wrapper.vm.saving).toBe(false);
expect(window.toastr.error).toHaveBeenCalled();
expect(wrapper.emitted("update")[0][0]).toEqual([...contacts, newContact]);
})
.catch((error) => {
throw Error(error)
})
});
What I exppect my test to fail because I use assertion expect(1).toBe(2).
Instead I have a result like that:
I have spend like 5h trying different solutions to make this work, with no luck.
Can you explain to me what is going on here? Or at least point me to right direction.
After spending another day I am able to suplie solution to my problem.
I have modified jest-setup.js to be able to use async await.
- test("addContact", () => {
+ test("addContact", async () => {
... // no changes
- flushPromises()
- .then(() => {
+ await flushPromises();
expect(1).toBe(2); // <-- now test fail here as expected ;-)
expect(wrapper.vm.saving).toBe(false);
expect(window.toastr.error).toHaveBeenCalled();
expect(wrapper.emitted("update")[0][0]).toEqual([...contacts, newContact]);
})
- .catch((error) => {
- throw Error(error)
- })
});
Anthough this works correctly now, I still don't know why Promise didin't work. So any suggestions are still welcome :)

Jest, expected mock function to have been called, but it was not called

Testing lifecycle methods when a VueJS component renders on the transition group.
I've been writing tests for lifecycle methods when the component renders on the transition group of the following VueJS component I've made little progress on getting it to work and would appreciate advice regarding this. I also tried switching between shallow mounting and mounting the component though that seemed to make no difference.
import { shallowMount } from '#vue/test-utils';
import StaggeredTransition from '../src/index';
const staggeredTransitionWrapper = componentData =>
shallowMount(StaggeredTransition, {
...componentData,
});
const staggeredTransition = staggeredTransitionWrapper();
describe('StaggeredTransition.vue', () => {
it('should render a staggered transition component', () => {
expect(staggeredTransition.element.tagName).toBe('SPAN');
expect(staggeredTransition.html()).toMatchSnapshot();
});
it('should mock calling the enter method', () => {
const enterMock = jest.fn();
StaggeredTransition.methods.enter = enterMock;
const staggeredTransitionWrapper2 = componentData =>
shallowMount(StaggeredTransition, { ...componentData });
const staggeredTransition2 = staggeredTransitionWrapper2({
slots: {
default: '<h1 :key="1">Staggered transition test</h1>',
},
});
expect(enterMock).toBeCalled();
});
});
Code for the StaggeredTransition component
<template>
<transition-group
:tag="tag"
:name="'staggered-' + type"
:css="false"
appear
#before-enter="beforeEnter"
#enter="enter"
#leave="leave"
>
<slot />
</transition-group>
</template>
<script>
const { log } = console;
export default {
name: 'StaggeredTransition',
props: {
type: {
type: String,
options: ['fade', 'slide'],
required: false,
default: 'fade',
},
tag: {
type: String,
required: false,
default: 'div',
},
delay: {
type: Number,
required: false,
default: 100,
},
},
methods: {
beforeEnter(el) {
console.log('beforeEnter');
el.classList.add(`staggered-${this.type}-item`);
},
enter(el, done) {
console.log('enter');
setTimeout(() => {
el.classList.add(`staggered-${this.type}-item--visible`);
done();
}, this.getCalculatedDelay(el));
},
leave(el, done) {
console.log('leave');
setTimeout(() => {
el.classList.remove(`staggered-${this.type}-item--visible`);
done();
}, this.getCalculatedDelay(el));
},
getCalculatedDelay(el) {
console.log('getCalculatedDelay');
if (typeof el.dataset.index === 'undefined') {
log(
'data-index attribute is not set. Please set it in order to
make the staggered transition working.',
);
}
return el.dataset.index * this.delay;
},
},
};
</script>

Unit testing Sails/Waterline models with mocha/supertest: toJSON() issue

I'm setting up unit tests on my Sails application's models, controllers and services.
I stumbled upon a confusing issue, while testing my User model. Excerpt of User.js:
module.exports = {
attributes: {
username: {
type: 'string',
required: true
},
[... other attributes...] ,
isAdmin: {
type: 'boolean',
defaultsTo: false
},
toJSON: function() {
var obj = this.toObject();
// Don't send back the isAdmin attribute
delete obj.isAdmin;
delete obj.updatedAt;
return obj;
}
}
}
Following is my test.js, meant to be run with mocha. Note that I turned on the pluralize flag in blueprints config. Also, I use sails-ember-blueprints, in order to have Ember Data-compliant blueprints. So my request has to look like {user: {...}}.
// Require app factory
var Sails = require('sails/lib/app');
var assert = require('assert');
var request = require('supertest');
// Instantiate the Sails app instance we'll be using
var app = Sails();
var User;
before(function(done) {
// Lift Sails and store the app reference
app.lift({
globals: true,
// load almost everything but policies
loadHooks: ['moduleloader', 'userconfig', 'orm', 'http', 'controllers', 'services', 'request', 'responses', 'blueprints'],
}, function() {
User = app.models.user;
console.log('Sails lifted!');
done();
});
});
// After Function
after(function(done) {
app.lower(done);
});
describe.only('User', function() {
describe('.update()', function() {
it('should modify isAdmin attribute', function (done) {
User.findOneByUsername('skippy').exec(function(err, user) {
if(err) throw new Error('User not found');
user.isAdmin = false;
request(app.hooks.http.app)
.put('/users/' + user.id)
.send({user:user})
.expect(200)
.expect('Content-Type', /json/)
.end(function() {
User.findOneByUsername('skippy').exec(function(err, user) {
assert.equal(user.isAdmin, false);
done();
});
});
});
});
});
});
Before I set up a policy that will prevent write access on User.isAdmin, I expect my user.isAdmin attribute to be updated by this request.
Before running the test, my user's isAdmin flag is set to true. Running the test shows the flag isn't updated:
1) User .update() should modify isAdmin attribute:
Uncaught AssertionError: true == false
This is even more puzzling since the following QUnit test, run on client side, does update the isAdmin attribute, though it cannot tell if it was updated, since I remove isAdmin from the payload in User.toJSON().
var user;
module( "user", {
setup: function( assert ) {
stop(2000);
// Authenticate with user skippy
$.post('/auth/local', {identifier: 'skippy', password: 'Guru-Meditation!!'}, function (data) {
user = data.user;
}).always(QUnit.start);
}
, teardown: function( assert ) {
$.get('/logout', function(data) {
});
}
});
asyncTest("PUT /users with isAdmin attribute should modify it in the db and return the user", function () {
stop(1000);
user.isAdmin = true;
$.ajax({
url: '/users/' + user.id,
type: 'put',
data: {user: user},
success: function (data) {
console.log(data);
// I can not test isAdmin value here
equal(data.user.firstName, user.firstName, "first name should not be modified");
start();
},
error: function (reason) {
equal(typeof reason, 'object', 'reason for failure should be an object');
start();
}
});
});
In the mongoDB console:
> db.user.find({username: 'skippy'});
{ "_id" : ObjectId("541d9b451043c7f1d1fd565a"), "isAdmin" : false, ..., "username" : "skippy" }
Yet even more puzzling, is that commenting out delete obj.isAdmin in User.toJSON() makes the mocha test pass!
So, I wonder:
Is the toJSON() method on Waterline models only used for output filtering? Or does it have an effect on write operations such as update().
Might this issue be related to supertest? Since the jQuery.ajax() in my QUnit test does modify the isAdmin flag, it is quite strange that the supertest request does not.
Any suggestion really appreciated.