I'm playing around learning XState and wanted to include an action in a machine that would just log the current state to console.
Defining a simple example machine like so, how would I go about this? Also note the questions in the comments in the code.
import { createMachine, interpret } from "xstate"
const sm = createMachine({
initial: 'foo',
states: {
foo: {
entry: 'logState', // Can I only reference an action by string?
// Or can I add arguments here somehow?
on: {
TOGGLE: {target: 'bar'}
}
},
bar: {
entry: 'logState',
on: {
TOGGLE: {target: 'foo'}
}
}
}
},
{
actions: {
logState(/* What arguments can go here? */) => {
// What do I do here?
}
}
});
I know that actions are called with context and event as arguments but I don't see a way to get the current state from either of those. Am I missing something here?
For a simple use case like yours, you could try recording the state on transition.
let currentState;
const service = interpret(machine).onTransition(state => {
if (state.value != currentState) {
// TODO: terminate timer if any and start a new one
currentState = state.value;
}
});
Then use the value in your actions.
See more here: https://github.com/statelyai/xstate/discussions/1294
Actions receive three arguments - context, event and meta. meta have property state, which is current state.
import { createMachine } from "xstate";
let metaDemo = createMachine(
{
id: "meta-demo",
initial: "ping",
states: {
ping: {
entry: ["logStateValues"],
after: { TIMEOUT: "pong" },
},
pong: {
entry: ["logStateValues"],
after: { TIMEOUT: "ping" },
},
},
},
{
delays: {
TIMEOUT: 3000,
},
actions: {
logStateValues(ctx, event, meta) {
if (meta.state.matches("ping")) {
console.log("It's PING!");
} else if (meta.state.matches("pong")) {
console.log("And now it's PONG");
} else {
console.log(
`This is not supposed to happen. State is: ${meta.state
.toStrings()
.join(".")}`
);
}
},
},
}
);
I want to execute logout function for one time and dropDown function for multiple iterations. Which changes shoul I need in below code.
executors: {
logout: {
type: 'per-vu-iterations',
exec: 'logout',
vus: 1,
iterations: 1,
startTime: '30s',
maxDuration: '1m',
tags: { my_tag: 'LOGOUT'},
},
}};
export function logout() {
group('Logout API', () => {
loginFunctions.logout_api();
})
}
export function dropDown() {
group('Drop Down API', () => {
loginFunctions.dropDown_api();
})
}
export default function () {
logout();
dropDown();
}
Also without default function it's not working. getting executor default: function 'default' not found in exports this error
Not sure where you saw executors, that was the old name of the option, before #1007 was merged and released. The new and correct name is scenarios: https://k6.io/docs/using-k6/scenarios
So, to answer your question, the code should look somewhat like this:
import http from 'k6/http';
import { sleep } from 'k6';
export let options = {
scenarios: {
logout: {
executor: 'per-vu-iterations',
exec: 'logout',
vus: 1, iterations: 1,
maxDuration: '1m',
tags: { my_tag: 'LOGOUT' },
},
dropDown: {
executor: 'per-vu-iterations',
exec: 'dropDown',
vus: 10, iterations: 10, // or whatever
maxDuration: '1m',
tags: { my_tag: 'LOGOUT' },
},
}
};
export function logout() {
console.log("logout()");
sleep(1);
// ...
}
export function dropDown() {
console.log("dropDown()");
sleep(1);
// ...
}
Though, depending on your use case, the best place for the logout() code might actually be in the teardown() lifecycle function? See https://k6.io/docs/using-k6/test-life-cycle for more details
I created a custom directive and it's working good, but when I run the mocha test for the component where I use this custom directive I receive this warning message [Vue warn]: Failed to resolve directive: scroll-text, tell me please how to fix that
test file:
import { shallowMount } from "#vue/test-utils"
import { scrollText } from "z-common/services"
import ZSourcesList from "./ZSourcesList"
Vue.use(scrollText)
const stubs = [
"z-text-field",
"v-progress-circular",
"v-icon",
"z-btn"
]
describe("ZSourcesList.vue", () => {
const sources = []
for (let i = 0; i < 20; i++) {
sources.push({
field: "source",
// format numbers to get 2 diggit number with leading zero 1 -> 01
value: `cluster-${i.toLocaleString('en-US', { minimumIntegerDigits: 2, useGrouping: false })}`,
__typename: "SuggestV2Result"
})
}
it("displays 'No matching sources found' if there are no sources", () => {
const wrapper = shallowMount(ZSourcesList, {
mocks: {
$apollo: {
queries: {
suggestions: {
loading: false,
},
},
},
},
stubs,
sync: false,
data() {
return {
suggestions: [],
}
},
})
expect(wrapper.find(".notification .z-note")).to.exist
})
})
Try registering your custom directive on a local vue instance and then mounting to that local vue instance.
import { shallowMount, createLocalVue } from "#vue/test-utils"
import { scrollText } from "z-common/services"
import ZSourcesList from "./ZSourcesList"
const localVue = createLocalVue()
localVue.use(scrollText) // Register the plugin to local vue
const stubs = [
"z-text-field",
"v-progress-circular",
"v-icon",
"z-btn"
]
describe("ZSourcesList.vue", () => {
const sources = []
for (let i = 0; i < 20; i++) {
sources.push({
field: "source",
// format numbers to get 2 diggit number with leading zero 1 -> 01
value: `cluster-${i.toLocaleString('en-US', { minimumIntegerDigits: 2, useGrouping: false })}`,
__typename: "SuggestV2Result"
})
}
it("displays 'No matching sources found' if there are no sources", () => {
const wrapper = shallowMount(ZSourcesList, {
mocks: {
$apollo: {
queries: {
suggestions: {
loading: false,
},
},
},
},
localVue, // Mount this component to localVue
stubs,
sync: false,
data() {
return {
suggestions: [],
}
},
})
expect(wrapper.find(".notification .z-note")).to.exist
})
})
Using a local vue instance instead of the global in test cases will also prevent polluting the global vue instance and help to prevent side effects in other test cases.
Trying to test if the following function gets called correctly.
Here is the function, using Jest and Enzyme for React Js:
changeCurrentColumn = (e) => {
{
if(e.target.value !== 'default')
{
const id = e.target.value
const name = this.props.defaultData[e.target.value].caption
this.props.setCurrentColumn({ name, id })
}
}
}
Here is the test:
it('changeCurrentColumn function test', () => {
wrapper.setProps({
defaultData:[{
caption:"test"
},],
}),
wrapper.update();
The error is pointing to ({target:'test'})) :
expect(wrapper.instance().changeCurrentColumn({target:'test'})).toBeDefined();
})
What can I add after 'test' in order to read the property of 'caption'?
Thanks.
You can change the type of the defaultData to object something like this.
it('changeCurrentColumn function test', () => {
const event = {
target: {
value: "caption"
}
}
wrapper.setProps({
defaultData:{
caption:"test"
},
}),
wrapper.update();
expect(wrapper.instance().changeCurrentColumn(event)).toBeDefined()
});
or pass value as 0 to get rest from the existing defaultData.
it('changeCurrentColumn function test', () => {
const event = {
target: {
value: 0
}
}
wrapper.setProps({
defaultData:[{
caption:"test"
}],
}),
wrapper.update();
expect(wrapper.instance().changeCurrentColumn(event)).toBeDefined()
});
I have an angular application and I need to do some unit testing on some methods with Jasmine. IN this case I do a unit test on a select list. So that the select list will not be empty.
The method looks like this:
createStatusOptions(listValueoptions: OptionModel[], resources: any): OptionModel[] {
const processStatusOptions = listValueoptions.map(listValueOption => {
listValueOption.value = `${caseStatusEnum.inProgress}_${listValueOption.value}`;
listValueOption.selected = true;
return listValueOption;
});
const caseStatusEnumKeys = Object.keys(caseStatusEnum).filter(key => !isNaN(Number(key)));
const enumOptions = this.optionService.createOptions(
new ConfigOptionModel({ source: caseStatusEnumKeys, resources, resourcesModel: enumResourcesModel, isCustomEnum: true, }));
return [
this.getEnumOption(enumOptions, caseStatusEnum.submitted, true),
...processStatusOptions,
this.getEnumOption(enumOptions, caseStatusEnum.closed),
];
}
private getEnumOption(options: OptionModel[], enumType, isSelected = false): OptionModel {
const option = options.filter(enumOption => enumOption.value === `${enumType}`)[0];
option.selected = isSelected;
return option;
}
And I have the unit test like this:
it('should create status options when there ar list value options are provided', () => {
optionService.options = [
{
value: caseStatusEnum.submitted.toString(),
},
{
value: caseStatusEnum.inProgress.toString(),
},
{
value: caseStatusEnum.closed.toString(),
},
] as OptionModel[];
// tslint:disable-next-line:max-line-length
const result = service.createStatusOptions(optionService.options, [[103], [104], [105] ]);
console.log(result);
expect(result.length).toBe(2);
expect(result).toEqual([{ value: '103', selected: true }, { value: '105', selected: false }]);
});
But I get an error like this:
Services: CaseService > should create status options when there ar list value options are provided
TypeError: Cannot set property 'selected' of undefined
at <Jasmine>
at CaseService.getEnumOption (http://localhost:9878/src/app/case/src/services/case.service.ts?:130:9)
at CaseService.getEnumOption [as createStatusOptions] (http://localhost:9878/src/app/case/src/services/case.service.ts?:109:22)
at UserContext.<anonymous> (http://localhost:9878/src/app/case/src/services/case.service.spec.ts?:149:32)
at ZoneDelegate.../../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9878/E:/Projects/Source/Repos/VLR/Web/vlrworkspace/node_modules/zone.js/dist/zone.js?:388:1)
at ProxyZoneSpec.push.../../node_modules/zone.js/dist/proxy.js.ProxyZoneSpec.onInvoke (http://localhost:9878/E:/Projects/Source/Repos/VLR/Web/vlrworkspace/node_modules/zone.js/dist/proxy.js?:128:1)
at ZoneDelegate.../../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9878/E:/Projects/Source/Repos/VLR/Web/vlrworkspace/node_modules/zone.js/dist/zone.js?:387:1)
at Zone.../../node_modules/zone.js/dist/zone.js.Zone.run (http://localhost:9878/E:/Projects/Source/Repos/VLR/Web/vlrworkspace/node_modules/zone.js/dist/zone.js?:138:1)
at runInTestZone (http://localhost:9878/E:/Projects/Source/Repos/VLR/Web/vlrworkspace/node_modules/zone.js/dist/jasmine-patch.js?:145:1)
at UserContext.<anonymous> (http://localhost:9878/E:/Projects/Source/Repos/VLR/Web/vlrworkspace/node_modules/zone.js/dist/jasmine-patch.js?:160:1)
at <Jasmine>
So my question is: How to solve this?
Thank you
if I do this:
console.log(optionService.options);
I get this output:
Array(3)
0: {value: "103", selected: true}
1: {value: "104"}
2: {value: "105", selected: false}
length: 3
__proto__: Array(0)
this is the file:
import { fakeAsync, tick } from '#angular/core/testing';
import { FormServiceMock, MultiFileUploadServiceMock } from 'afw/forms/testing';
import { AfwHttp } from 'afw/generic-services';
import { AfwHttpMock, OptionServiceMock } from 'afw/generic-services/testing';
import { OptionModel, SearchResultModel } from 'afw/models';
import { FeedbackStoreServiceMock } from 'afw/store-services/testing';
import { RouterMock } from 'afw/testing';
import { PagingDataModel, TableSortDataModel } from 'afw/ui-components';
import { caseOwnerEnum, caseStatusEnum, caseTypeEnum, MultiFileUploadResourcesModel } from 'lr/models';
import { Observable, observable } from 'rxjs';
import { CaseTypeInfoModel } from 'support-shared/base/models';
import { CaseTypeInfoStoreServiceMock } from 'support-shared/base/services/case-type-info-store.service.mock';
import { CaseFormComponent } from '../case-base/src/case-form/case-form.component';
import { CaseBaseModel, CaseReferenceModel } from '../models';
import { CaseService } from './case.service';
let service: CaseService;
let afwHttpMock: AfwHttpMock;
// tslint:disable-next-line:prefer-const
let formServiceMock: FormServiceMock;
let multiFileUploadService: MultiFileUploadServiceMock;
let router: RouterMock;
let feedbackStoreService: FeedbackStoreServiceMock;
let optionService: OptionServiceMock;
let caseTypeInfoStoreService: CaseTypeInfoStoreServiceMock;
// tslint:disable-next-line:prefer-const
let component: CaseFormComponent;
fdescribe('Services: CaseService', () => {
beforeEach(() => {
afwHttpMock = new AfwHttpMock();
multiFileUploadService = new MultiFileUploadServiceMock();
router = new RouterMock();
feedbackStoreService = new FeedbackStoreServiceMock();
optionService = new OptionServiceMock();
caseTypeInfoStoreService = new CaseTypeInfoStoreServiceMock();
service = new CaseService(afwHttpMock as any, multiFileUploadService as any, router as any,
feedbackStoreService as any, optionService as any, caseTypeInfoStoreService as any);
});
it('should create an instance', () => {
expect(service).toBeTruthy();
});
it('should get case reference details', () => {
afwHttpMock.setupOnlyResponse({ type: caseTypeEnum.revisionRequest, details: { bsn: 'bsnLabel' } }, 200);
const d = service.getCaseReferenceDetails('spinnerMessage', { reference: '112314121', type: caseTypeEnum.revisionRequest });
d.subscribe(r => {
expect(r.details.length === 1);
expect(r.details[0].key).toBe('bsn');
expect(r.details[0].value).toBe('bsnLabel');
expect((r.details[0] as any).resourceKey).toBe('bsn');
});
afwHttpMock.returnSuccessResponse();
});
// tslint:disable-next-line:no-identical-functions
it('should get case reference details with full response', () => {
afwHttpMock.setupOnlyResponse({ body: { type: caseTypeEnum.revisionRequest, details: [{ key: 'hoi' }] } }, 200);
const d = service.getCaseReferenceDetailsFullResponse('spinnerMessage', { reference: '100001075', type: caseTypeEnum.revisionRequest });
// tslint:disable-next-line:no-commented-code
// tslint:disable-next-line:no-identical-functions
/* let result;
d.subscribe(r => {
result = r;
}); */
d.subscribe(r => {
expect(r.ok === true);
expect(r.body.details[0].key).toBe('hoi');
});
afwHttpMock.returnSuccessResponse();
// expect(result.ok === true);
// expect(result.)
});
// tslint:disable-next-line:no-commented-code
it('shoud get case type info configuration that is used on various views when snapshot exists', () => {
let result99: Observable<CaseTypeInfoModel[]>;
result99 = service.getCaseTypeInfo('spinner') as Observable<CaseTypeInfoModel[]>;
const response = [{ mock: 'mock' } as any];
service['caseTypeInfoSnapshot'] = response;
service.getCaseTypeInfo('spinner').subscribe(i => {
expect(i).toEqual(response);
});
});
// tslint:disable-next-line:no-identical-functions
it('shoud get case type info configuration that is used on various views when snapshot doesnt exists', () => {
let result99: Observable<CaseTypeInfoModel[]>;
const spy = spyOn(caseTypeInfoStoreService, 'addCaseTypeInfoToStore');
result99 = service.getCaseTypeInfo('spinner') as Observable<CaseTypeInfoModel[]>;
const response = [{ mock: 'mock' } as any];
service['caseTypeInfoSnapshot'] = response;
// caseTypeInfoStoreService..subscribe((result) => { expect(result).toBe(false); });
result99.subscribe((result) => {
expect(response).toEqual(response);
});
afwHttpMock.setupOnlyResponse(result99, 200);
afwHttpMock.returnSuccessResponse();
});
it('should create status options when no list value options are provided', () => {
optionService.options = [
{
value: caseStatusEnum.submitted.toString(),
},
{
value: caseStatusEnum.inProgress.toString(),
},
{
value: caseStatusEnum.closed.toString(),
},
] as OptionModel[];
// tslint:disable-next-line:no-commented-code
// const spy = spyOn(service, 'createStatusOptions');
const result = service.createStatusOptions([], {});
expect(result.length).toBe(2);
expect(result).toEqual([{ value: '103', selected: true }, { value: '105', selected: false }]);
// tslint:disable-next-line:no-commented-code
// const response = [{ mock: 'mock' } as any];
// expect(spy).toBe(result);
});
it('should create status options when there ar list value options are provided', () => {
optionService.options = [
{
value: caseStatusEnum.submitted.toString(),
},
{
value: caseStatusEnum.inProgress.toString(),
},
{
value: caseStatusEnum.closed.toString(),
},
] as OptionModel[];
// tslint:disable-next-line:max-line-length
const result = service.createStatusOptions(optionService.options, 103);
console.log(optionService.options);
expect(result.length).toBe(2);
expect(result).toEqual([{ value: '103', selected: true }, { value: '105', selected: false }]);
});
it('should get case reference without details', () => {
afwHttpMock.setupOnlyResponse({}, 200);
const spy = spyOn(afwHttpMock, 'post').and.callThrough();
const model = new CaseReferenceModel({ reference: '112314121', type: caseTypeEnum.revisionRequest });
const d = service.getCaseReferenceDetails('spinnerMessage', model);
d.subscribe(r => {
expect(r).toBeDefined();
});
expect(spy).toHaveBeenCalledWith('api/support/cases/get-reference-details', model, 'spinnerMessage');
afwHttpMock.returnSuccessResponse();
});
it('should add case reference without details', () => {
afwHttpMock.setupOnlyResponse({}, 200);
const spy = spyOn(afwHttpMock, 'post').and.callThrough();
const model = new CaseReferenceModel({ reference: '112314121', type: caseTypeEnum.revisionRequest });
const d = service.addCase('spinnerMessage', model as any);
d.subscribe(r => {
expect(r).toBeDefined();
});
expect(spy).toHaveBeenCalledWith('api/support/cases', model, 'spinnerMessage');
afwHttpMock.returnSuccessResponse();
});
it('should search for cases', () => {
const formModel: any = { makeQueryString: () => 'name=test' };
const pagingModel = new PagingDataModel({ currentPage: 10, itemsPerPage: 20 });
const sortModel = new TableSortDataModel({ columnName: 'kol', isDescending: false });
const spy = spyOn(afwHttpMock, 'get').and.callThrough();
const mockData = [
new CaseBaseModel({
id: 100000001,
type: caseTypeEnum.revisionRequest,
status: caseStatusEnum.inProgress,
substatus: 5266,
verdict: null,
owner: caseOwnerEnum.caseManager,
dateSubmitted: '02-02-2009',
dateClosed: '',
reference: 'aaa',
}),
];
const setupResponse = new SearchResultModel<CaseBaseModel>();
setupResponse.result = mockData;
setupResponse.totalResultCount = 27;
afwHttpMock.setupOnlyResponse(setupResponse, 200);
let response: SearchResultModel<CaseBaseModel>;
service.search(formModel, sortModel, pagingModel, 'spinnerText').subscribe(result => {
response = result;
});
afwHttpMock.returnOnlyResponse();
expect(spy).toHaveBeenCalledWith('api/support/cases?name=test&columnName=kol&isDescending=false¤tPage=10&itemsPerPage=20',
'spinnerText');
expect(response).toEqual(setupResponse);
expect(response.result[0].getResourceForStatus).toBeDefined();
});
it('should save documents', fakeAsync(() => {
const spy = spyOn(multiFileUploadService, 'syncFilesWithBackend').and.callThrough();
const spyRouter = spyOn(router, 'navigate').and.callThrough();
const spyFeedback = spyOn(feedbackStoreService, 'addSuccessMessageOnMainPortal');
service.saveDocuments(1, [{} as any], MultiFileUploadResourcesModel.keys, '../', { key: 'da', value: 'fa' });
expect(spy).toHaveBeenCalledWith('api/support/cases/1/documents', [{}],
MultiFileUploadResourcesModel.keys.bijlageToevoegenSpinnerTekst,
MultiFileUploadResourcesModel.keys.bijlageVerwijderenSpinnerTekst
);
tick();
expect(spyRouter).toHaveBeenCalledWith(['../']);
expect(spyFeedback).toHaveBeenCalled();
}));
it('should not save documents if there are no documents in array', fakeAsync(() => {
const spy = spyOn(multiFileUploadService, 'syncFilesWithBackend').and.callThrough();
const spyRouter = spyOn(router, 'navigate').and.callThrough();
const spyFeedback = spyOn(feedbackStoreService, 'addSuccessMessageOnMainPortal');
service.saveDocuments(1, [], MultiFileUploadResourcesModel.keys, '../', { key: 'da', value: 'fa' });
expect(spy).not.toHaveBeenCalled();
tick();
expect(spyRouter).toHaveBeenCalledWith(['../']);
expect(spyFeedback).toHaveBeenCalled();
}));
it('should save documents and report errors', fakeAsync(() => {
multiFileUploadService.setResponse([{}, { error: {} }]);
spyOn(multiFileUploadService, 'makeWarningMessageForUnsyncedFiles').and.returnValue('mock');
const spyRouter = spyOn(router, 'navigate').and.callThrough();
const spyFeedback = spyOn(feedbackStoreService, 'addWarningMessageOnMainPortal');
const spy = spyOn(multiFileUploadService, 'syncFilesWithBackend').and.callThrough();
service.saveDocuments(1, [{} as any], MultiFileUploadResourcesModel.keys, '../', { key: 'da', value: 'fa' });
expect(spy).toHaveBeenCalledWith('api/support/cases/1/documents', [{}],
MultiFileUploadResourcesModel.keys.bijlageToevoegenSpinnerTekst,
MultiFileUploadResourcesModel.keys.bijlageVerwijderenSpinnerTekst
);
tick();
expect(spyRouter).toHaveBeenCalledWith(['../']);
expect(spyFeedback).toHaveBeenCalled();
}));
it('should get case by id', () => {
const id = 66208014;
const setupResponse = new CaseBaseModel({
id,
dateSubmitted: '',
owner: caseOwnerEnum.caseManager,
reference: 'ksjhkjshdf',
status: caseStatusEnum.submitted,
type: caseTypeEnum.revisionRequest,
});
afwHttpMock.setupOnlyResponse(setupResponse, 200);
service.getCase(id, 'spinner').subscribe(r => {
expect(r).toEqual(setupResponse);
});
afwHttpMock.returnSuccessResponse();
});
it('edit the case with model', () => {
const spy = spyOn(service, 'editCase').and.callThrough();
const caseUpdate = new CaseBaseModel({
id: 100001075,
dateSubmitted: '',
owner: caseOwnerEnum.caseManager,
reference: 'ksjhkjshdf',
status: caseStatusEnum.submitted,
type: caseTypeEnum.revisionRequest,
});
service.editCase('spinner', caseUpdate);
expect(spy).toHaveBeenCalledWith('spinner', caseUpdate);
expect(caseUpdate.id).toEqual(100001075);
});
});
Based on what you showed so far, my guess is that the options parameter passed to getEnumOption() is undefined, which is causing the error you see. A quick console.log(options) within getEnumOption() would verify this.
If your code is working fine otherwise, but only failing in the test then I would make a second guess that you haven't properly mocked/spiedOn this.optionService.createOptions() since it sets up the options parameter that is potentially undefined. That would have been done earlier in the .spec file - if you post the whole file then that would help others who read your question to determine if this is the case.
Update with Stackblitz
I put all your code into a Stackblitz to test it. There was a lot of code I didn't have access to that I just guessed at the functionality of. However, I did discover a few things.
First, when you are testing you appear to be using the same variable both for the mock of the return expected by this.optionService.createOptions() as well as in the call to service.createStatusOptions() - which is likely not what you want to do.
Here is the code snippet I am talking about:
optionService.options = [
{
value: caseStatusEnum.submitted.toString(),
},
{
value: caseStatusEnum.inProgress.toString(),
},
{
value: caseStatusEnum.closed.toString(),
},
] as OptionModel[];
// tslint:disable-next-line:max-line-length
const result = service.createStatusOptions(optionService.options, [[103], [104], [105] ]);
When I called it this way in the Stackblitz I ran into a mutability issue - you are changing the data within the members of the objects inside the array, which will change it whereever that variable is accessed. To overcome this in the Stackblitz I made two copies of the data, one to use in the mock returnValue and another completely separate array of objects for the call to service.createStatusOptions(). Also, I am not familiar with the way you are mocking your service call, so I replaced it with a simple Jasmine spy in the Stackblitz.
Feel free to have a look at what I produced. Perhaps it will be helpful.