Angular 2 mocking method and calling - testing

I'm trying to call a mocked service and update the user variable, but when I call the mocked service nothing changes?
I am generally struggling with testing, if there are any good resources to learn with angular2 I'm all ears.
let users = [{ name: 'Test', lastname: 'User' }, { name: 'Test2', lastname: 'User2' }];
let addedUsers = [{ name: 'blah', lastname: 'blah' },{ name: 'Test', lastname: 'User' }, { name: 'Test2', lastname: 'User2' }];
describe('Component: UserList', () => {
beforeEach(() => {
userServiceStub = {
getUsers: () => {
return Observable.of(users);
},
getUser: () => {
return Observable.of(user);
},
addUser: () => {
users.push({ name: 'blah', lastname: 'blah' });
}
};
TestBed.configureTestingModule({
declarations: [UserListComponent],
imports: [HttpModule],
providers: [
{provide: UsersService, useValue: userServiceStub },
{ provide: Router, useClass: RouterStub }
]
});
app = fixture.debugElement.componentInstance;
userService = fixture.debugElement.injector.get(UsersService);
it('should call addUser on button click', () => {
let spy = spyOn(userService, 'addUser');
userService.addUser('argument');
fixture.detectChanges();
expect(users).toEqual(addedUsers);
});
});

Related

XState.js How to send context to a machine?

I am new to XState.js.
I want to use a simple ID in my context. How do I update the context using machine.send()?
const fetchMachine = Machine(
{
id: 'test',
initial: 'init',
context: {
id: '',
},
states: {
init: {
on: {
LOGIN: 'fetch',
},
},
fetch: {
on: {
LOGOUT: 'init',
},
},
}
})
const machine = interpret(fetchMachine).start()
How do I pass an ID to the context?
This does NOT do the trick:
machine.send({ type: 'LOGIN', id })
You have to use the assign action to update the context. I've updated your example to the following:
import { Machine, assign } from 'xstate';
// Action to assign the context id
const assignId = assign({
id: (context, event) => event.id
});
export const testMachine = Machine(
{
id: 'test',
initial: 'init',
context: {
id: '',
},
states: {
init: {
on: {
LOGIN: {
target: 'fetch',
actions: [
assignId
]
}
},
},
fetch: {
on: {
LOGOUT: 'init',
},
},
}
},
{
actions: { assignId }
}
);
Now once you call the following:
import { testMachine } from './machines';
const testService = interpret(testMachine).start();
testService.send({type: 'LOGIN', id: 'test' });
//or testService.send('LOGIN', { id: 'test'});
the action assignId will assign data from the event to your context

vue-router : Navigation not routing to correct path

I am using vuex and vue-router. I have a client's zone, when you try to loggin, you should go to the profil. But it's not happend.
routes: [
{
path: "/area-do-cliente",
name: "customerZone",
redirect: "/area-do-cliente/profile",
component: () =>
import(
/* webpackChunkName: "ClientZone" */ "#/scenes/ClientZone/views/ClientZone.vue"
),
children: [
{
path: "login",
name: "login",
component: () => import("#/scenes/ClientZone/components/LogIn.vue"),
props: true,
meta: { clientZoneLogin: true }
},
{
path: "profile",
name: "profile",
component: () => import("#/scenes/ClientZone/components/Profile.vue"),
meta: { clientZoneAuthRequired: true, clientZoneLogin: true },
props: true
},
]
}
]
router.beforeEach((to, from, next) => {
if (to.matched.some(_to => _to.meta.clientZoneLogin))
if (store.getters.personData.status == "1") {
console.log("01");
router({ name: "profile" });
} else {
console.log("2");
next();
}
} else {
console.log("3");
next();
}
}
});
So I still have in console => 2. It is mean that I pass the first condition, but I shouldnt. I have status 1, and the response of this data is when you click the login button (there is a event).
methods: {
doLogin: function() {
this.error = "";
let loginData = {
login: this.login,
password: this.password
};
this.isLoading = true;
this.errorMessage = "";
co.postLogin(loginData)
.then(data => {
this.$store.commit("personData", data.data); // here I push data to store
)}
}
}
Any ideas where is mistake?

Computed property depends on vuex store. How to update the cached value?

The value of this.$store.state.Auth.loginToken is modified by one of its child components. The initial value of this.$store.state.Auth.loginToken is undefined. But still, the update in its value has no effect in the cached value of navItems thus it always returns the second value.
computed: {
navItems () {
return this.$store.state.Auth.loginToken != undefined ?
this.items.concat([
{ icon: 'add', title: 'Add new journal entry', to: '/' },
{ icon: 'power_settings_new', title: 'Logout', to: '/logout'}
]) :
this.items.concat([
{ icon: 'play_arrow', title: 'Login', to: '/login' }
])
}
}
Is there a better way to keep a watch on this.$store.state.Auth.loginToken so that it can be used same as navItems?
I created a basic example of how you can use vuex getters and Auth token (codepen):
const mapGetters = Vuex.mapGetters;
var store = new Vuex.Store({
state: {
Auth: {
loginToken: ''
},
menuItems: [
{ icon: 'home', title: 'Home', to: '/' },
{ icon: 'about', title: 'About', to: '/about' },
{ icon: 'contact', title: 'Contact', to: '/contact' }
]
},
mutations: {
SET_LOGIN_TOKEN(state, data) {
state.Auth.loginToken = 1
}
},
getters: {
menuItems(state, getters) {
if(state.Auth.loginToken !== '') {
return state.menuItems.concat([{
icon: 'profile', title: 'Profile', to: '/profile'
}])
}
return state.menuItems
},
loggedIn(state) {
return state.Auth.loginToken !== ''
}
},
actions: {
doLogin({commit}) {
commit('SET_LOGIN_TOKEN', 1)
}
}
});
new Vue({
el: '#app',
store,
data: function() {
return {
newTodoText: "",
doneFilter: false
}
},
methods: {
login() {
this.$store.dispatch('doLogin')
}
},
computed: {
...mapGetters(['menuItems', 'loggedIn'])
}
})
This is just an example so you can ignore the actual login action. Also, the store should be a directory, the getters, mutations and actions should have their own files which are then imported in the index.js in the store like in this example

populate mongoose key as part of object

EDIT
minimal reproduction repo
It's easier to explain in code than English.
The following code works, but it feels like there's gotta be an easier, more MongoDBy/mongoosy way ...
// recipeModel.js, relevant part of the schema
equipments: [{
_id: {
type: Schema.Types.ObjectId,
ref: 'equipments',
},
quantity: {
type: Number,
required: true,
},
}],
// recipeController.js
const equipmentsWorkaround = recipe => Object.assign({}, recipe.toObject(), {
equipments: recipe.toObject().equipments.map(equip => ({
quantity: equip.quantity,
name: equip._id.name,
_id: equip._id._id,
})),
})
const getItem = (req, res, next) => {
Recipes.findById(req.params.id)
.populate('equipments._id')
.then(equipmentsWorkaround) // <--- ugh ...
.then(recipe => res.json(recipe))
.catch(next)
}
I know how to do a "conventional" ref in mongoose, but is what I'm after here even possible in mongo?
desired outcome:
equipments: [
{
quantity: 1,
name: "Pan",
_id: 'some mongo object here...'
},
{
quantity: 3,
name: "Big Knife",
_id: 'some mongo object here...'
}
]

Mocha Testing a Nested Model

Trying to write a test for a nested model but can't get it working:
Model:
const EmployeeSchema = new mongoose.Schema({
firstName: {type: String, required: true},
lastName: { type: String, required: true}
});
const CompanySchema = new mongoose.Schema({
name: { type: String, required: true },
streetAddress: { type: String, required: true },
country: { type: String, required: true },
employees:[EmployeeSchema]
}, { timestamps: true});
Controller:
function create(req, res, next) {
const company = new Company({
name: req.body.name,
streetAddress: req.body.streetAddress,
country: req.body.country
});
company.employees.push(req.employees);
company.save()
.then(savedCompany => res.json(savedCompany))
.catch(e => next(e));
}
Test:
describe('## Company APIs', () => {
let company = {
name: "Test Company",
streetAddress: "123 Fake Street",
country: "A Country"
};
company.employees.push({firstName: "Jane", lastName: "Doe"});
describe('# POST /api/company', () => {
it('should create a new company', (done) => {
request(app)
.post('/api/company')
.send(company)
.expect(httpStatus.OK)
.then((res) => {
expect(res.body.name).to.equal(company.name);
expect(res.body.streetAddress).to.equal(company.streetAddress);
expect(res.body.country).to.equal(company.country);
company = res.body;
done();
})
.catch(done);
});
});
The above gives: TypeError: Cannot read property 'push' of undefined
I've tried a few other things but this is the most promising result, for some reason I just can't seem to populate the embedded model as part of setting up the unit test.
I ended up resolving this, hopefully this helps someone in the future.
Test:
it('should associate an employee with the company', (done) => {
var employee = new Employee();
company.employees.push(employee);
request(app)
.put(`/api/company/${company._id}`)
.send(company)
.expect(httpStatus.OK)
.then((res) => {
expect(res.body.employees).to.be.an('array')
expect(res.body.employees).to.contain(employee.id)
done();
})
.catch(done);
});
Controller:
Adding this to handle multiple additions:
if (req.body.employees != null) {
req.body.employees.forEach(function(employee) {
company.employees.push(employee);
}, this);
}