How to unit test whether form validations are added dynamically in Jest?
I'm struggling with how to write a unit test for checking if my form validations were added to a FormControl in Angular.
It's a simple form with a phone
field. I subscribe to that field and add or remove a validator depending on the field being empty or not. The code works as intended but I don't get how to test the subscription and valueChanges portion.
How can I write a Jest test to cover the following subscription?
this.form = new FormGroup({
name: new FormControl('', [Validators.required]),
phone: new FormControl(''),
)}
// Validate that phone is either blank or a valid phone number
this.form.controls.phone.valueChanges.subscribe(data => {
if (data.phone !== '') { // Add validations to phone
this.form.controls.phone.setValidators([Validators.pattern(PHONE_VALIDATOR)]);
} else { // Remove validation from phone when field is blank
this.form.controls.phone.clearValidators();
}
});
Best Attempt at Testing. It passes but my coverage is showing 0% for the .valueChanges section.
it('should add phone validation when phone is not blank', () => {
component.ngOnInit();
let phoneField = component.form.controls.phone;
phoneField.setValidators([Validators.pattern(PHONE_VALIDATOR), beetweenxAndyNumericChars(7, 11, false)]);
phoneField.setValue('123');
component.save();
expect(phoneField.valid).toBeFalsy();
});
The above test runs but doesn't improve my code coverage. I'm stuck on how to improve the code coverage
See also questions close to this topic
-
How to create an order from a cart in Mongoose and delete cart
I am trying to: a) Create an order from my current cart b) Populate the response of the order with the selected object attributes in the "select" c) Finally, I would like to delete the cart.
I have tried the below but here are my problems:
- the result response does not come back populated even if the
prevProductsQuantity
constant is indeed populated - I created the function cartClear to receive the cart id and delete the document from the model but it is not working.
- I am able to create many "same" orders so I think i need to manage with a conditiona but not sure where.
The response I am looking for:
{ "success": true, "data": { "_id": "607ed5c425ae063f7469a807", "userId": "6071aed0ed7ec9344cf9616c", "productsQuantity": [ { "_id": "607f2507994d5e4bf4d91879", "productId": { "productRef": "463b8bb7-6cf6-4a97-a665-ab5730b69ba2", "productName": "table", "brand": "boehm llc", "price": 713, "__v": 0, "createdAt": "2021-04-09T18:31:43.430Z", "updatedAt": "2021-04-09T18:31:43.430Z" }, "quantity": 2, "updatedAt": "2021-04-21T15:12:51.894Z", "createdAt": "2021-04-21T15:12:51.894Z" } ], "__v": 0 }
++ THE DELETION OF THE CART ++
The current response I am getting:
{ "success": true, "data": { "state": "pending-payment", "_id": "6080507a0c758608d0dc585c", "userId": "6071aed0ed7ec9344cf9616c", "totalPrice": 1426, "productsQuantity": [ { "_id": "607f2507994d5e4bf4d91879", "productId": "60709d8f24a9615d9cff2b69", "quantity": 2, "updatedAt": "2021-04-21T15:12:51.894Z", "createdAt": "2021-04-21T15:12:51.894Z" } ], "createdAt": "2021-04-21T16:19:06.056Z", "updatedAt": "2021-04-21T16:19:06.056Z", "__v": 0 }
** AND THE CART IS NOT DELETING **
this is the code I typed for these purposes.
Any guidance is super appreciated
router.post('/', [isAuthenticated], async (req, res, next) => { try { const cart = await CartModel.findOne({ userId: req.user }).populate({ path: 'productsQuantity.productId', select: { price: 1, brand: 1, productName: 1, productRef: 1, pictures: 1 } }); const prevProductsQuantity = cart .get('productsQuantity') .map((el) => el.toObject()); const totalPriceByProduct = prevProductsQuantity.map( (product) => product.productId.price * product.quantity ); const totalPrice = totalPriceByProduct.reduce(function (a, b) { return a + b; }); const result = await OrderModel.create({ userId: req.user, totalPrice: totalPrice, state: 'pending-payment', productsQuantity: prevProductsQuantity }); const cartClear = (id) => CartModel.deleteOne({ _id: id._id }); res.status(200).json({ success: true, data: result }); cartClear(`${cart._id.toString()}`); } catch (error) { next(error); } });
- the result response does not come back populated even if the
-
How to fetch SQL data using api and use that data in react-native-svg charts? I am having an API that I want to use to fetch data and display
I am fetching some data using an api. Inside that api there are SQL queries that are executed. I have api that will be used to fetch data or execute these queries. I want to know how can I replace my chart's static data with dynamic data that will be fetched from api.
Here is my
TabDashboardDetail.js
where I am fetching title for all charts based on api data:import React from 'react'; import DefaultScrollView from '../components/default/DefaultScrollView'; import ChartView from '../components/default/ChartView'; import CogniAreaChart from '../components/CogniAreaChart'; import { areaChartData } from '../chartData'; const TabDashboardDetail = ({ navigation, route }) => { const tabsConfig = route.params.tabsConfig; return ( <DefaultScrollView> {tabsConfig.components.map((comp, index) => { return ( <ChartView key={index} title={comp.name}> <CogniAreaChart areaChartData={areaChartData} height={200} /> </ChartView> ); })} </DefaultScrollView> ); }; export default TabDashboardDetail;
Here is my
CogniAreaChart.js
which is chart file that is currently being rendered:/* eslint-disable react-native/no-inline-styles */ import React from 'react'; import { View } from 'react-native'; import { AreaChart, YAxis, XAxis } from 'react-native-svg-charts'; import * as shape from 'd3-shape'; const CogniAreaChart = ({ areaChartData, visibility, ...props }) => { const xAxis = areaChartData.message.map((item) => item[Object.keys(item)[0]]); const areaChartY1 = areaChartData.message.map( (item) => item[Object.keys(item)[1]], ); return ( <View style={{ height: props.height, flexDirection: 'row', }}> <YAxis data={areaChartY1} contentInset={{ marginBottom: 20 }} svg={{ fill: 'grey', fontSize: 12, }} /> <View style={{ flex: 1 }}> <AreaChart style={{ flex: 1 }} data={areaChartY1} contentInset={{ top: 20, bottom: 20 }} curve={shape.curveNatural} svg={{ fill: 'rgba(134, 65, 244, 0.8)' }} /> <XAxis style={{ height: 20 }} data={areaChartY1} formatLabel={(value, index) => xAxis[index]} contentInset={{ left: 30, right: 30 }} svg={{ fill: 'grey', fontSize: 12, rotation: 35, originY: 5, y: 15, }} /> </View> </View> ); }; export default CogniAreaChart;
Here is areachartData that is currently being used in
CogniAreaChart.js
:export const areaChartData = { message: [ { year: '2018', quantity: 241.01956823922, sales: 74834.12976954, }, { year: '2019', quantity: 288.57247706422, sales: 80022.3050176429, }, ], status: 'success', };
I have the API that I will replace with the example if anyone suggests.
-
Sort,Filter,search in Javascript array
Given values for acctData and balances below, write a function that returns only an array of account numbers, and accepts the following optional parameters:
- user - sortBy (accepts "acctNum" or "balance") - sortDirection (accepts "asc" or "desc"; default to asc)
Execute your function and output the results as an array in console log for the following scenarios:
a) filtered by Bob b) filtered by Charlie c) sorted by acctNum d) filtered by Alice; sorted by balance ascending
acctData = [ { "acctNum": "AAA - 1234", "user": "Alice" }, { "acctNum": "AAA - 5231", "user": "Bob" }, { "acctNum": "AAA - 9921", "user": "Alice" }, { "acctNum": "AAA - 8191", "user": "Alice" } ]; balance = { "AAA - 1234": 4593.22, "AAA - 9921": 0, "AAA - 5231": 232142.5, "AAA - 8191": 4344 };
-
Ionic/Angular + Firebase with emulator on Android device = auth/network-request-failed
I have to improve existing Ionic app using firebase as auth method for users login into app. For purpose don't touch existing list of previously registered users in production firebase app, I have to create new locally using firebase emulator. After adding and setup local auth & DB emulator extensions, I have added this into
app.module.ts
:if (window.location.hostname === 'localhost') { firebase.auth().useEmulator('http://localhost:9099'); }
After continue with adding new UI and logical features, using web browser to test my work, I switch to Android platform. After running app and executing
firebase.auth().signInWithEmailAndPassword(email, password);
I got following error:
{ "code": "auth/network-request-failed", "message": "A network error (such as timeout, interrupted connection or unreachable host) has occurred." }
I have spent some time to figure out what is wrong, why all previously worked fine in browser, but not on real device.
-
Promise not resolved even when the request is done
I have a function that returns a promise from an HTTP Request:
getToken(id: string): Promise<string> { return this.service .getToken(id) .pipe( take(1) //catchError(() => of(null)) ) .toPromise(); }
And I want to know if the call failed (I won't have any token in this case), but the promise doesn't seem to complete because the IF statement is never reached if an error has occurred (BadRequest, Forbidden, etc.)
... const token = await this.getToken(id); if (!token) { console.log('no token'); } ...
But if I'm using
catchError(() => of(null))
, the promise completes even if the call failed and I can't understand the behavior. -
Socket.io connection to different mySQL tables, Angular front end
I have an angular app that displays a table of data (mySQL database) and updates whenever anything is added to the database. I feel that I should add I'm very inexperienced, i know angular but trying to learn more about backened operations.
I'm using a websocket (socket.io) on a node.js server to achieve this. It works fine but I'd like to add a second unrelated table of data that will appear in a different part of my app. . Should I set up another websocket to achieve this? Or can one websocket interact with 2 different table in the one database.
All of the SQL queries are handled in the backend and look like this.
// Create MySQLEvents const instance = new MySQLEvents(connection, { startAtEnd: true // to record only new binary logs }); await instance.start(); instance.addTrigger({ name: 'Monitor all SQL Statements', expression: 'mydb.*', // listen to database statement: MySQLEvents.STATEMENTS.ALL, onEvent: e => { currentData = e.affectedRows; let newData; switch (e.type) { case "INSERT": database.table('products') .withFields(['id', 'title', 'quantity', 'price']) .sort({id: -1}) .getAll() .then(prods => { data = prods; io.sockets.emit('update', {prods: [...data]}); }) .catch(err => console.log(err)); .....
My front end just accepts and displays the incoming data. I'd be unsure of how to add a second socket to it.
Here is my socket.service.ts in angular.
export class SocketService { constructor(private socket: Socket) { } getInitialData() { return this.createObserver('initial'); } getUpdatedData() { return this.createObserver('update'); } private createObserver(event: string) { return this.socket.fromEvent(event); }
and here is the component.ts
export class DashboardComponent implements OnInit, OnDestroy { private subs: Subscription[] = []; localData: any[] = []; constructor(private socketService: SocketService) { } ngOnInit() { this.subs.push( this.socketService.getInitialData().subscribe((data: ServerResponse) => { this.localData = data.prods; }) ); this.subs.push( this.socketService.getUpdatedData().subscribe((data: ServerResponse) => { this.localData = data.prods; }) ); } ngOnDestroy() { this.subs.forEach(s => s.unsubscribe()); } } interface ServerResponse { prods: any[]; type?: string; }
I just iterate over localData to display the table.
My ideal outcome would be to have the one websocket with multiple endpoints. I just don't know how to handle this with mySQL events.
Similarly if I had 2 completely separate websockets I'm unsure how to handle that on the angular side.
-
Test cases using react-testing-library
I'm facing some difficulties while writing test cases using the react-testing library.
Adding my code to simplify the question: Here, I'm creating a component that includes two conditions if isWithField is false or true. I'm not able to write the test case for these two conditions.
const BorderedCheckbox = ({ classname, isWithField, ...otherProps }: Props):Node => isWithField ? ( <CheckboxWithField className={className} {...otherProps} /> ):( <Checkbox className={className} {...otherProps} /> ) BorderedCheckbox.defaultProps = { isWithField: false };
I'm adding my test cases also with mock, please point out the mistake
describe('<BorderedCheckbox />', () => { test('should render correctly with defaultConfig', () => { const BorderedCheckboxComponent = render( <ThemeProvider> <BorderedCheckbox {...defaultConfig} /> </ThemeProvider> ); expect(BorderedCheckboxComponent).toMatchSnapshot(); }); test('should render correctly if wrapped withField', () => { const BorderedCheckboxComponent = render( <ThemeProvider> <Form> <BorderedCheckbox {...defaultConfig} isWithField /> </Form> </ThemeProvider> ); expect(BorderedCheckboxComponent).toMatchSnapshot(); }); }); //mock export const defaultConfig = { isWithField: false }; //Second test case is failing with this message Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead
-
How to mock an object with Jasmine
On my DI setup I have a singleton class that has a couple of functions which I would like to disable when testing.
In some other frameworks / languages I would provide a mock of that object whose implementation of the functions is empty.
I'd like to do something like
TestBed.configureTestModule({ imports: […], providers: [ { provide: MyClass, useValue: spyAllFunctions(MyClass).and.stub() } ] })
I've already taken a look at
spyAllFunctions
but doesn't get me quite there (or I am misusing it), for now I am just providing the stub myself, but I wonder if there's a better way than{ provide: MyClass, deps: [MyClassDependency], useFactory: (dep) => ({ method1: () => { }, method2 () => { } }) }
Thanks!
-
Detect console errors as error and make test fail
Is it possible to detect console errors in unit tests with karma runner and mark the unit test case as failed. In my current project I have hundreds of tests and the project isn't in a clean state. When I run the unit tests with
ng test
I get hundreds or maybe even thousands of console message likeERROR: ''mat-icon' is not a known element: 1. If 'mat-icon' is an Angular component, then verify that it is part of this module. 2. If 'mat-icon' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.'
but all test cases pass successfully. There is no hint which test cases cause these problems. I tried to manually step through the tests by replacing
it(
withfit(
, check each test and fix it but it takes too long. I'd like to make each test fail that contains an error in the console log like it can be done in E2E tests withafterEach(async () => { // Assert that there are no errors emitted from the browser const logs = await browser.manage().logs().get(logging.Type.BROWSER); expect(logs).not.toContain(jasmine.objectContaining({ level: logging.Level.SEVERE, } as logging.Entry)); });
so that the CD/CI pipeline fails and the developer has to fix the tests.
The configuration of the unit tests is the default. It's the karma runner with jasmine and a headless Chrome.
I searched the karma configuration but couldn't find it. Is this even possible with unit tests? If it's possible, is it possible to configure this at one place and not to touch all hundreds of test files?
To reproduce my problem create a new Angular project with
ng new sandbox
. Change theapp.component.ts
toimport { Component } from '@angular/core'; @Component({ selector: 'my-app', template: '<mat-icon></mat-icon>' }) export class AppComponent { }
Change the
app.component.spec.ts
to:import { TestBed } from '@angular/core/testing'; import { AppComponent } from './app.component'; describe('AppComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [ AppComponent ], }).compileComponents(); }); it('should create the app', () => { const fixture = TestBed.createComponent(AppComponent); const app = fixture.componentInstance; expect(app).toBeTruthy(); }); });
Delete
app.components.html
andapp.component.css
. Runng test
.Even though the app doesn't work because Material isn't installed the test won't fail. It passes but it prints error logs. I get the output:
> ng test ⠋ Generating browser application bundles...21 04 2021 18:18:06.826:WARN [karma]: No captured browser, open http://localhost:9876/ 21 04 2021 18:18:06.830:INFO [karma-server]: Karma v6.1.2 server started at http://localhost:9876/ 21 04 2021 18:18:06.831:INFO [launcher]: Launching browsers Chrome with concurrency unlimited ⠙ Generating browser application bundles (phase: building)...21 04 2021 18:18:06.835:INFO [launcher]: Starting browser Chrome ✔ Browser application bundle generation complete. 21 04 2021 18:18:09.656:WARN [karma]: No captured browser, open http://localhost:9876/ 21 04 2021 18:18:09.696:INFO [Chrome 89.0.4389.114 (Linux x86_64)]: Connected on socket w7tUMYWxN3pJ5tdBAAAB with id 77883839 ERROR: 'NG0304: 'mat-icon' is not a known element: 1. If 'mat-icon' is an Angular component, then verify that it is part of this module. 2. If 'mat-icon' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.' Chrome 89.0.4389.114 (Linux x86_64): Executed 0 of 1 SUCCESS (0 secs / 0 secs) ERROR: 'NG0304: 'mat-icon' is not a known element: 1. If 'mat-icon' is an Angular component, then verify that it is part of this module. Chrome 89.0.4389.114 (Linux x86_64): Executed 1 of 1 SUCCESS (0.072 secs / 0.023 secs) TOTAL: 1 SUCCESS
I'd like this test to fail.
-
What would be a proper "man or boy test" to check if a given language has good support for functional programming idioms?
The original Man or boy test was devised by Donald Knuth to to distinguish compilers that correctly implemented "recursion and non-local references" from those that did not. I'm not asking about this original version.
My issue is, sometimes people argue if not-pure-functional language A or B has better support for functional programming idioms. It's common, for example to read complaints about Python lacking good support for those who like to write code in functional style, due to lambdas limited to one line, or lack of tail call optimization, that blows recursive functions after a depth around 1000. I think this is not a exaustive list.
This context bears my question: In the spirit of the original Man or boy test, what would be a simple routine involving the main functional programming idioms, like deep recursion, lambdas, et cetera, that we coud try to implement in any language to separate the man-functional from the boy-functional?
-
Accessing Page Date On Failure
I have a need to access page data if a test fails in cypress, but I am unable to perform any cy.get() operations. The code operates in the following way:
- A test executes.
- At some point, it attempts to get an element using cy.get(). If the element does not exist, other elements will appear that contain error message data. The error message data elements may or may not exist.
- If the element being sought does not exist, Cypress.on('fail', (error, runnable) => ...) is invoked. It is in this code block I would like to access the error message data.
Is there any way to use Cypress to access the html elements in the on fail handler? Or is there a different approach I should be taking?
-
react-native regex to match with digit + one character
I need to test if my string match with a certain format, this one : "20/04/1980"
So I tried to write a regex but I test only digits with it :.match(/\d{2}\.\/\d{2}.\/\d{4}/)
How can I test the match with also the "/" character ?
-
Jest - simulate keydown "ArrowUp" and invoking a function
I have a component that renders input with controls. The controls act as adding/minusing the value by 1. I have
input
withonKeyDown={handleKeyDown}
const addUp = () => { let next = value + 1; if (next > max) { next = max; } setValue(next); onChange(next); }; const minusDown = () => { let next = value - 1; if (next < min) { next = min; } setValue(next); onChange(next); }; function handleKeyDown() { const input = event.currentTarget; switch (event.key) { case "ArrowUp": addUp(); break; case "ArrowDown": minusDown(); break; } event.preventDefault(); }
I want to mock the handleKeyDown above. Here's what I tried so far:
let defaultProps: Props; beforeEach(() => { defaultProps = { max: 100, min: 1, onChange: noop, value: 10, }; }); describe("controls", () => { it("should call addUp and increase value", () => { const addUp = jest.fn(); const wrapper = mount(<MyComponent {...defaultProps} />); const input = wrapper.find("input"); input.simulate("keydown", { key: "ArrowUp", keyCode: 38, preventDefault() {}, which: 38 }); expect(addUp).toBeCalledTimes(1); expect(input.prop("value")).toBe(11); }); });
this is the error I'm getting:
expect(jest.fn()).toBeCalledTimes(expected) Expected number of calls: 1 Received number of calls: 0
Does anyone have any suggestion on how to do this properly?
-
Jest snapshot tests: arbitrary change of generated ids
We use Jest snapshot testing with an Angular 11 project. The problem is that some generated element ids seem to suddenly change, without any actual code change. It seems that on the first run, the ids are always the same, but on subsequent runs, they sometimes change. Example from a jest snapshot difference:
The corresponding template
<div> <mat-form-field fxFlex="100"> <mat-label>{{'COMMON_LABEL_WINSCRIBE_USERTYPE' | translate}}</mat-label> <mat-select formControlName="wsUserType"> <mat-option [value]="userTypes.Author">{{'MANAGEMENT_LABEL_WS_USERTYPE_AUTHOR' | translate}}</mat-option> <mat-option [value]="userTypes.Typist">{{'MANAGEMENT_LABEL_WS_USERTYPE_TYPIST' | translate}}</mat-option> </mat-select> </mat-form-field> </div>
I already tried stopping all node.exe processes between test runs, but that doesn't help.
Also, when using
ng test
, the problem doesn't arise. But withng test
I don't get IDE integration.Does anybody have another idea what could be the problem?