Exploring Angular 2 testing with TypeScript: multiple occurrences of specifications in Jasmine

Recently, I followed a tutorial on testing an Angular 2 application which can be found at: https://angular.io/docs/ts/latest/guide/testing.html

Upon completing the 'First app test' section and moving to 'unit-tests.html', I noticed that my spec reports were being displayed multiple times:

https://i.sstatic.net/XCIXA.png
Even though I did not make any changes to the tutorial code, here is the contents of my unit-tests.html for reference:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="content-type" content="text/html;charset=utf-8">
    <title>Ng App Unit Tests</title>

    <link rel="stylesheet" href="node_modules/jasmine-core/lib/jasmine-core/jasmine.css">

    <script src="node_modules/jasmine-core/lib/jasmine-core/jasmine.js"></script>
    <script src="node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js"></script>
    <script src="node_modules/jasmine-core/lib/jasmine-core/boot.js"></script>
</head>
<body>
<!-- #1. add the system.js library -->
<script src="node_modules/systemjs/dist/system-polyfills.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>

<script>
    // #2. Configure systemjs to use the .js extension
    //     for imports from the app folder
    System.config({
        packages: {
            'app': {defaultExtension: 'js'}
        }
    });

    // #3. Import the spec file explicitly
    System.import('app/hero.spec')

    // #4. wait for all imports to load ...
    //     then re-execute `window.onload` which
    //     triggers the Jasmine test-runner start
    //     or explain what went wrong.
            .then(window.onload)
            .catch(console.error.bind(console));
</script>
</body>

</html>

hero.spec.ts

import {Hero} from './hero';

describe('Hero', () => {
    it('has name', () => {
        let hero:Hero = {id: 1, name: 'Super Cat'};
        expect(hero.name).toEqual('Super Cat');
    });
    it('has id', () => {
        let hero:Hero = {id: 1, name: 'Super Cat'};
        expect(hero.id).toEqual(1);
    });
});

Could you possibly shed some light on what may have caused this issue?

Answer №1

After noticing that my previous comment was deleted, I am sharing my final solution to resolve the issue at hand. This solution effectively addresses the problem, with the only minor inconvenience being that initially, the HTML page may display "No specs found," but once all modules are loaded, it showcases all specifications and their respective results.

The source of this solution is the book titled "Angular 2 Development with TypeScript v7 MEAP" from Manning Early Access Program:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="content-type" content="text/html;charset=utf-8">
    <title>Unit Tests</title>
    <link rel="stylesheet" href="node_modules/jasmine-core/lib/jasmine-core/jasmine.css">
    <!-- #1. Polyfills -->
    <script src="node_modules/reflect-metadata/Reflect.js"></script>
    <!-- #2. Zone.js dependencies -->
    <script src="node_modules/zone.js/dist/zone.js"></script>
    <script src="node_modules/zone.js/dist/async-test.js"></script>
    <script src="node_modules/zone.js/dist/fake-async-test.js"></script>
    <!-- #3. Add specs dependencies -->
    <script src="app/treeNode.spec.ts"></script>
    <script src="app/template.spec.ts"></script>
    <script src="app/services/tree.side.service.spec.ts"></script>
    <script src="app/services/tree.service.spec.ts"></script>
    <!-- #4. Add Jasmine dependencies -->
    <script src="node_modules/jasmine-core/lib/jasmine-core/jasmine.js"></script>
    <script src="node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js"></script>
    <script src="node_modules/jasmine-core/lib/jasmine-core/boot.js"></script>
    <script src="node_modules/jasmine-expect/dist/jasmine-matchers.js"></script>
    <!-- #5. Add the system.js library -->
    <script src="node_modules/systemjs/dist/system.src.js"></script>
    <script>System.packageWithIndex = true;</script>
    <script src="systemjs.config.js"></script>

    <script>
        // #6. Configure SystemJS to use the .js extension for imports from the app folder
        System.config({
            packages: {
                'app': {defaultExtension: 'js'}
            }
        });

        var SPEC_MODULES = [
            'app/treeNode.spec',
            'app/template.spec',
            'app/services/tree.side.service.spec',
            'app/services/tree.service.spec'
        ];

        /**
         * #7. Import the spec files explicitly
         */
        Promise.all([
            System.import('@angular/core/testing'),
            System.import('@angular/platform-browser-dynamic/testing')
        ])
        .then(function (modules) {
            var testing = modules[0];
            var testingBrowser = modules[1];
            testing.TestBed.initTestEnvironment(testingBrowser.BrowserDynamicTestingModule, testingBrowser.platformBrowserDynamicTesting());
            return Promise.all(SPEC_MODULES.map(function (module) {
                return System.import(module);
            }));
        })
        .then(window.onload)
        .catch(console.error.bind(console));
    </script>
</head>
<body>
</body>
</html>

I trust you will find this solution beneficial in your current scenario.

Answer №2

After looking into the issue, I have found a potential solution. Check out this example on Plunker for reference: https://plnkr.co/edit/abc123?p=preview.

One key difference that may be causing the problem is the order in which the SystemJS and Jasmine library files are included:

<script src="https://cdns.example.com/system-polyfills.js"></script>
<script src="https://cdns.example.com/system.js"></script>

<script src="https://cdns.example.com/jasmine/2.4.1/jasmine.js"></script>
<script src="https://cdns.example.com/jasmine/2.4.1/jasmine-html.js"></script>
<script src="https://cdns.example.com/jasmine/2.4.1/boot.js"></script>

Answer №3

I'm experiencing the same issue with my test case showing up 3 times instead of just once.

To resolve this, I decided to eliminate the .then(window.onload) function call within the Promise for System.import('app/hero.spec').

It seems like SystemJS has the ability to load hero.spec.js before the browser triggers the window load event, rendering the subsequent window.onload call unnecessary.

Answer №4

When using lite-server, I have noticed that this strange behavior can be reproduced. The example provided on the Angular2 site uses live-server (please note the "v" instead of "t"). When using live-server, Jasmine correctly renders my tests. Could it be possible that there are some issues with browser-sync injecting some magic into the HTML?

Answer №5

After encountering a similar issue, I found that importing the core shim library resolved it completely. It's important to note that you should import it as the first script on your page.

 <script src="https://unpkg.com/core-js/client/shim.min.js"></script>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the process for initializing the default/prefilled value of an input element within a mat-form-field when the page loads?

I'm looking to create an HTML element where the default data (stored in the variable 'preselectedValue') is automatically filled into the input field when the component loads. The user should then be able to directly edit this search string. ...

What is the best way to effectively handle the proxying of objects across multiple levels?

As illustrated in a Stack Overflow thread, utilizing Proxy objects is an effective method for monitoring changes in an object. But what if you need to monitor changes in subobjects? In such cases, you will also have to proxy those subobjects. I am curren ...

Encountering a Typescript issue when trying to access props.classes in conjunction with material-ui, react-router-dom

I could really use some help with integrating material-ui's theming with react-router-dom in a Typescript environment. I'm running into an issue where when trying to access classes.root in the render() method, I keep getting a TypeError saying &a ...

Encountering difficulties when attempting to npm install @angular/common/http

Up until yesterday, I had no issues downloading the @angular/common/http package. However, today when I try to run npm i I keep encountering this error message: npm ERR! code EINVALIDPACKAGENAME npm ERR! Invalid package name "@angular/common/http": ...

Are AngularJS Material Components compatible with Angular2?

Recently, I inherited a web app project from a former colleague who has since moved on from our company. The web app was originally built using Angular2 as its framework, but it seems that the previous developer attempted to incorporate AngularJS Material ...

The slot="error" feature in Ionic 6 Angular does not function properly when used with ion-checkbox and ion-radio-group components

I am encountering an issue with slot error handling. It seems to be working fine for most input fields, but it is not functioning correctly for ion-checkbox and ion-radio-group components. In my .ts file, the form is built like this: private buildForm() { ...

Organize nested tuples and maintain their sequence

I am attempting to achieve a functionality similar to the following: type Type = object; type TypeTuple = readonly Type[]; function flattenTuples<T extends readonly (Type | TypeTuple)[], R = Flatten<T>>(...tuples: T): R { // code to flatten ...

Moving the marker does not update the address

When the dragend event is triggered, the Getaddress function will be called in the following code: Getaddress(LastLat, LastLng , marker,source){ this.http.get('https://maps.googleapis.com/maps/api/geocode/json?latlng='+LastLat+ &apos ...

Examining interconnected services' dependencies

Looking to test out AService, which has dependencies on BService and CService. The dependency chain looks like this: AService --> BService --> CService The constructor for AService is as follows: constructor( private bService: BService ) {} The ...

Hide the MaterialTopTabNavigator from view

Is there a way to hide the react native MaterialTopTabNavigator on the screen while still maintaining the swiping functionality between screens? I want the user to be able to swipe between screens but not see the tab navigator itself. const Tab = creat ...

Encountering an issue with npm packages failing to install due to an

There seems to be an issue with npm. Kindly report this error at: Error code: EINVALIDTYPE Error type: Typeerror Argument #5: Expected object but received string Error occurred in: inflatableChild (C:\Program Files\nodejs\node_mod ...

Using Angular's ternary operator to handle keyboard events for both the space bar and tab key

I am looking to validate the type of input before applying restrictions on space and tab keydown events. Here is what I have attempted: <input (keydown.space)="locationUi.location_type=='video' ? $event.preventDefault() : false" (keydown.tab ...

Theme customization in Material UI includes the addition of a custom color. However, this custom color is missing from the control values in Story

Currently in my project, I am utilizing a stack that includes React 18, TypeScript, MUI 5, and Storybook 6.5. I have been attempting to incorporate custom colors into my MUI Theme and have them reflect in Storybook's dropdown options for the color p ...

Angular CLI integrated with Isotope version 2

I am facing difficulties when using the isotope-layout module with Angular CLI. To install the module, I used the command: npm install isotope-layout --save After installation, I added the script in my .angular-cli.json file: "scripts": [ ... " ...

Angular 4 HTTP Requests Failing to Retrieve JSON Data

I am currently working with the following method in my Typescript: allPowerPlants(onlyActive: boolean = false, page: number = 1): PowerPlant[] { const params: string = [ `onlyActive=${onlyActive}`, `page=${page}` ].join('&&apo ...

The real-time updates on an Angular 2 website can be seen across multiple devices simultaneously

Just getting started with Angular 2 and ran into an interesting issue. When setting up my website, NPM defaults the server to http://localhost:3000. To test the site on another computer, I tried accessing it using my IP address http://10.x.x.x:3000 and eve ...

Merge arrays values with Object.assign function

I have a function that returns an object where the keys are strings and the values are arrays of strings: {"myType1": ["123"]} What I want to do is merge all the results it's returning. For example, if I have: {"myType1": ["123"]} {"myType2": ["45 ...

What Angular Material Component is ideal for organizing horizontal outcomes effectively?

I am new to Angular Material and I'm exploring its capabilities. Is there a way to custom layout the MatTable Component to display three results horizontally instead of vertically, while still utilizing its paging and sorting features? If not, are th ...

Utilizing Angular's intelligent dichotomy of (container) and (presentational) components, integrating conditional logic, and effectively passing inputs throughout the

Imagine you have two simple components, A and B. Component C will only display either component A OR B, which is controlled by smart component D. D (smart) | C (simple) / \ A B (both simple) Both components A and B have 10 inputs each. Ther ...

What is the reason behind permitting void functions in the left part of an assignment in Typescript?

Take a look at this Typescript snippet: let action = function (): void { //perform actions }; let result = action(); What makes it suitable for the TypeScript compiler? ...