Unit testing with QUnit, Grunt and RequireJS - unit-testing

I am trying to get my Unit Tests working in Grunt, when I execute my index file in the browser the tests run successfully, however when I run the tests with grunt qunit it cannot recognise any tests.
I I am sure this is down to the way I am executing the tests, if for example I just include:
<script>
test('Always Fail', function() {
equal(5, 2, 'The return should be 2.');
});
</script>
In the head of my index.html test page, and then run Grunt I can see this test failing. However I am trying to load my tests using requireJS, which as I have said do work within the browser.
My index.html file looks like this:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Unit Testing</title>
<link rel="stylesheet" href="qunit/qunit.css">
<script data-main="../unittests" src="../lib/require.js"></script>
<script src="qunit/qunit.js"></script>
<script>
test('Always Fail', function() {
equal(5, 2, 'The return should be 2.');
});
</script>
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
</body>
</html>
I am assuming it is this line:
<script data-main="../unittests" src="../lib/require.js"></script>
That is causing the issue and not being loaded with grunt.
My unittests.js file looks like this:
require.config({
paths: {
'QUnit': 'test/qunit/qunit'
},
shim: {
'QUnit': {
exports: 'QUnit',
init: function() {
QUnit.config.autoload = false;
QUnit.config.autostart = false;
}
}
}
});
// require the unit tests.
require(
['QUnit', 'test/dummyTest'],
function(QUnit, dummyTest) {
// run the tests.
dummyTest.run();
// start QUnit.
QUnit.load();
QUnit.start();
}
);
Here is my gruntfile:
module.exports = function(grunt) {
// use grunt
var nocompress, optimize;
nocompress = grunt.option('nocompress') || false;
if(nocompress) {
optimize = 'none';
} else {
optimize = 'uglify';
}
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
requirejs: {
compile: {
options: {
appDir: "dev/",
baseUrl: "js",
mainConfigFile: "dev/js/bootstrap.js",
dir: "www",
optimize: optimize,
modules: [
{
name: "app"
}
]
}
}
},
qunit: {
all: ['dev/js/test/**/*.html']
},
jshint: {
options: {
curly: true,
eqeqeq: true,
eqnull: true,
browser: true,
globals: {
jQuery: true
},
},
uses_defaults: [
'dev/js/collections/*.js',
'dev/js/models/*.js',
'dev/js/views/*.js',
'dev/js/*.js', ]
}
});
// Load plugins
grunt.loadNpmTasks('grunt-contrib-qunit');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-requirejs');
// Default task(s).
grunt.registerTask('build-and-qunit', ['default', 'qunit']);
grunt.registerTask('default', ['jshint', 'requirejs']);
};

I've had some success using a structure similar to:
https://github.com/jonnyreeves/qunit-require
Disable QUnit from auto-running.
Require all the dependencies.
Run QUnit.

Related

How do I unit test a quasar app using Jest?

I have an quasar application that was generated with the quasar-cli.
How do I integrate a unit test into a test runner like Jest for an application like this?
I've added a this to my Jest configuration
"moduleNameMapper": {
"quasar": "<rootDir>/node_modules/quasar-framework"
}
Unfortunately, Jest reports back
Cannot find module 'quasar' from 'index.vue'
Here is the a snippet of the Vue file
<template>
<div style="padding-top: 20px" v-if="refund.type != null ">
<q-btn :label="'Issue ' + ( currency(refund.amount)) + ' Refund'" :disable="refund.amount <= 0" #click="issueRefund()" color="green" class="full-width" :loading="noteLoading" />
</div>
</template>
<script>
import { Notify } from "quasar"; // here is where I am using Quasar
issueRefund() {
this.noteLoading = true;
this.$axios
.post(`${BASE_URL}/issue_refund/?secret=${this.secret}`, {
refund: this.refund,
agent_email: this.userEmail,
order_id: this.selectedOrder.id,
agent_name: this.$route.query.user_name,
order_number: this.selectedOrder.order_number,
ticket_id: this.ticketId
})
.then(res => {
this.noteLoading = false;
if ((res.data.res === "success")) {
Notify.create({
position: "bottom",
type: "positive",
message: "Refund Issued."
});
this.selectedOrder = res.data.order;
this.resetRefundObj();
this.$refs.refundDiag.hide();
} else {
Notify.create({
position: "bottom",
type: "negative",
message: res.data.error
});
}
});
},
</script>
Integrating Jest with Quasar is quite straight-forward. You'll need two packages, babel-jest and jest.
yarn add jest babel-jest -D
After adding those two dependencies, create a jest.config.js file at the root of your project--here's where all the jest configuration goes.
Here's how the jest.config.js file should look like;
module.exports = {
globals: {
__DEV__: true,
},
verbose: false, // false since we want to see console.logs inside tests
bail: false,
testURL: 'http://localhost/',
testEnvironment: 'jsdom',
testRegex: './__unit__/.*.js$',
rootDir: '.',
testPathIgnorePatterns: [
'<rootDir>/components/coverage/',
'<rootDir>/test/cypress/',
'<rootDir>/test/coverage/',
'<rootDir>/dist/',
'<rootDir>/node_modules/',
],
moduleFileExtensions: ['js', 'json', 'vue'],
moduleNameMapper: {
'^vue$': 'vue/dist/vue.common.js',
'quasar': 'quasar-framework/dist/umd/quasar.mat.umd.js',
},
resolver: null,
transformIgnorePatterns: [
'node_modules/core-js',
'node_modules/babel-runtime',
'node_modules/vue',
],
transform: {
'^.+\\.js$': '<rootDir>/node_modules/babel-jest',
'.*\\.(vue)$': '<rootDir>/node_modules/vue-jest',
}
}
Then create a folder inside the root of your project called __unit__
Place a file called MyUnitTest.test.js inside the __unit__ folder. Now Jest picks up files from this folder.
The final touch would be to run the tests, simply add this to the package.json
"unit": "yarn run jest --config jest.config.js"
Boom! -- Now you may run yarn run unit or yarn run unit --watch and it should work.
Here's a sample of a Quasar component and Jest test.
import { createLocalVue, shallowMount } from '#vue/test-utils'
import Vuex from 'vuex'
import Quasar, * as All from 'quasar'
import CookieConsent from '#components/common/CookieConsent.vue'
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(Quasar, { components: All, directives: All, plugins: All })
describe('CookieConsent.vue', () => {
const wrapper = shallowMount(CookieConsent, {
localVue,
mocks: {
$t: () => {},
},
})
test('CookieConsent.vue mock should exist', () => {
expect(wrapper.exists()).toBe(true)
})
})
Hope you found this useful

How to include images with html-webpack-plugin + twig-loader?

I'm trying to include an image in my twig template just like simple tag, but it doesn't want to include. For build, I use HtmlWebpackPlugin and twig-loader;
But if I do the same with html-loader and html template - it works fine.
How to do it right with twig-loader?
my webpack-config:
const path = require( 'path' );
const HtmlWebpackPlugin = require( 'html-webpack-plugin' );
const PATHS = {
source: path.join( __dirname, './source' ),
build: path.join( __dirname, './build' )
};
module.exports = {
entry: `${ PATHS.source }/index.js`,
output: {
path: PATHS.build,
filename: 'webpack.bundle.js'
},
module: {
rules: [
{
test: /\.twig/,
loader: 'twig-loader'
},
{
test: /.*\.(gif|png|jpe?g)$/i,
use: [
{
loader: 'file-loader?name=[name].[ext]'
}
]
}
]
},
plugins: [
new HtmlWebpackPlugin( {
filename: 'index.html',
template: `${PATHS.source}/index.twig`,
} )
],
};
my twig template:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<img src="./chuck-norris.jpg" alt="">
</body>
</html>
my package.json:
{
"name": "htmlWebpackPlugin-twigLoader",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack"
},
"author": "evisotskiy",
"license": "ISC",
"devDependencies": {
"file-loader": "^0.11.2",
"html-loader": "^0.5.1",
"html-webpack-plugin": "^2.30.1",
"twig-loader": "^0.3.1",
"webpack": "^3.6.0"
}
}
my project's structure:
.
├── webpack.config.js
├── package.json
├── source
| ├──index.twig
| ├──index.js (empty)
| ├──chuck-norris.jpg
and when I execute npm run build I get dir:
├── build
| ├──index.html
| ├──webpack.bundle.js
without chuck-norris.jpg
And when I use html-loader instead twig-loader and html-template instead twig-template - image builds fine. But for my project, I need to use twig templates.
How to do it right with twig-loader?
I have found a solution. Instead of passing to HtmlWebpackPlugin as template the .twig-file directly, I passed as template a .js-file, inside of which I included a .twig file and image, and passed the image to the twig-template as a variable. Now the project looks like this:
my webpack-config:
const path = require( 'path' );
const HtmlWebpackPlugin = require( 'html-webpack-plugin' );
const PATHS = {
source: path.join( __dirname, './source' ),
build: path.join( __dirname, './build' )
};
module.exports = {
entry: `${ PATHS.source }/index.js`,
output: {
path: PATHS.build,
filename: 'webpack.bundle.js'
},
module: {
rules: [
{
test: /\.twig$/,
loader: 'twig-loader'
},
{
test: /.*\.(gif|png|jpe?g)$/i,
use: [
{
loader: 'file-loader?name=[name].[ext]'
}
]
}
]
},
plugins: [
new HtmlWebpackPlugin( {
filename: 'index.html',
template: `${PATHS.source}/index.twig.js`,
} )
],
};
my twig template:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<img src="{{ image.src }}" alt="{{ image.alt }}">
</body>
</html>
my index.twig.js:
const template = require( './index.twig' );
const image = {
src: require( './chuck-norris.jpg' ),
alt: "Chuck Norris"
};
module.exports = template( { image } );
my package.json:
{
"name": "htmlWebpackPlugin-twigLoader",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack"
},
"author": "evisotskiy",
"license": "ISC",
"devDependencies": {
"file-loader": "^0.11.2",
"html-webpack-plugin": "^2.30.1",
"twig-loader": "^0.3.1",
"webpack": "^3.6.0"
}
}
my project's structure:
.
├── webpack.config.js
├── package.json
├── source
| ├──index.twig
| ├──index.twig.js
| ├──index.js (empty)
| ├──chuck-norris.jpg
and when I execute npm run build I get builded project I expected:
├── build
| ├──index.html
| ├──chuck-norris.jpg
| ├──webpack.bundle.js
I faced the same problem recently. But I could not use a solution with handling URLs as parameters.
I've fixed it with extract-loader and html-loader.
Honestly, I don't know how it works, but it works.
This is a task for gulp.
See rules with test: /\.(jpeg|png|svg|jpg|gif)$/i, and test: /\.twig$/,
//region js
task('app:compile', function(){
return src(path.js.src)
.pipe(named())
.pipe(webpack({
mode: 'development',
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env'],
plugins: ['#babel/plugin-proposal-object-rest-spread']
}
}
},
{
test: /\.(scss|css)$/,
exclude: /(node_modules|bower_components)/,
use: [
{loader: "style-loader"},
{loader: "css-loader"},
{loader: "sass-loader"},
],
},
{
test: /\.(jpeg|png|svg|jpg|gif)$/i,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: '../images',
publicPath: 'images',
}
},
],
},
{
test: /\.twig$/,
use: [
'twig-loader',
'extract-loader',
{
loader:'html-loader',
},
],
},
{
test:/\.(woff|woff2|eot|ttf|otf)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: '../fonts',
publicPath: 'fonts',
}
},
],
},
],
},
devtool: 'source-map',
}))
.pipe(dest(path.js.dest));
});
//endregion
You can see the full version of gulpfile.js at the link
landing_scratch gulpfile.js
Hope it helps somebody who is googling the same questions.
If it's any help, I fixed this very same problem by simply changing the loader that processes the twig template files and adding "html-loader".
This would be the change:
{
test: /\.twig$/,
loader: 'twig-loader'
}
to
{
test: /\.twig$/,
exclude: /node_modules/,
use: [
'html-loader',
'twig-html-loader'
]
}
Of course, you must install those loaders previously if you didn't do it yet.
npm install twig-html-loader html-loader --save-dev
Henceforth, the images will be processed ;)
I incorporated Twig templates in my advanced SPA build with Vue CLI. The issue I faced was the same as the topic starter.
My Twig templates were compiled fine. But the images in the templates were not copied to build folder. And the paths in resulting output HTML files were not transformed into their Webpack-built-and-hashed paths.
At the same time the images in .vue files were copied and the images paths were updated.
To solve this I applied the core of the solution from #s.smsmsm above to my context. Thanks mate.
Briefly: you have to make Webpack to use 3 loaders to process your Twig templates. These are twig-loader, extract-loader and html-loader.
For this I made the specifig vue.config.js.
See it here with more explanations. The short version follows.
// vue.config.js
module.exports = {
runtimeCompiler: true,
outputDir: '.dist',
pages: {
main: {
entry: 'src/js/main.js',
template: 'src/views/pages/index.twig',
filename: 'index.html'
}
},
// Here is where the fix actually is made.
chainWebpack: config => {
config.module
.rule('twig')
.test(/\.twig$/)
.use('twig-loader')
.loader('twig-loader')
.end()
.use('extract-loader')
.loader('extract-loader')
.end()
.use('html-loader')
.loader('html-loader')
.end();
}
};
The Vue CLI config reference is here. Its chainWebpack option was used to modify the respective Webpack config part via vue.config.js. How to add a new loader doc is here.

vue-i18n single file component in laravel project

Given a Laravel 5.5 project, I want to use the "single file component" of the vue-i18n plugin. Documentation. It seems simple, but I can't get it to work.
app.js
import VueI18n from 'vue-i18n'
Vue.use(VueI18n)
const i18n = new VueI18n({
locale: 'en',
messages: {
"en": {
"word1": "hello world!"
}
}
})
Vue.component('test', require('./components/test.vue'));
const app = new Vue({ i18n, el: '#apps'});
components/test.vue
<template>
{{ $t('word1') }}
{{ $t('word2') }}
</template>
<i18n>
{
"en": {
"word2": "does this work?"
}
}
</i18n>
<script>
export default {
name: "test"
data() {
return {
locale: 'en'
}
},
mounted() {},
watch: {
locale (val) {
this.$i18n.locale = val
}
}
}
</script>
word1 is being replaced, however word2 is not. Placing bad syntax between the i18n-tags in the vue file, does NOT result in an error while compiling the files (npm run dev). This makes sense, because I'm missing the:
Taken from the documentation
module.exports = {
// ...
module: {
rules: [
...
This is supposed to go in the Webpack configuration. But, where is this file in laravel? All I can find is the webpack.mix.js, but placing this code in there, does not do much... Also making it mix.module.exports does not do the trick. Searching led me to this topic, but i'm not sure if he's asking the same as I am.
The problem: the i18n-tags aren't loaded. The solution is to add the code from the documentation.
My question: Where do I add this code?
To anyone stumbling upon the same problem, I proposed a change in the documentation:
https://github.com/kazupon/vue-i18n/pull/237
Laravel mix has its own rules for .vue files. To add the vue-i18n-loader, add the following to webpack.mix.js
mix.webpackConfig({
// ...
module: {
rules: [
{
// Rules are copied from laravel-mix#1.5.1 /src/builder/webpack-rules.js and manually merged with the ia8n-loader. Make sure to update the rules to the latest found in webpack-rules.js
test: /\.vue$/,
loader: 'vue-loader',
exclude: /bower_components/,
options: {
// extractCSS: Config.extractVueStyles,
loaders: Config.extractVueStyles ? {
js: {
loader: 'babel-loader',
options: Config.babel()
},
scss: vueExtractPlugin.extract({
use: 'css-loader!sass-loader',
fallback: 'vue-style-loader'
}),
sass: vueExtractPlugin.extract({
use: 'css-loader!sass-loader?indentedSyntax',
fallback: 'vue-style-loader'
}),
css: vueExtractPlugin.extract({
use: 'css-loader',
fallback: 'vue-style-loader'
}),
stylus: vueExtractPlugin.extract({
use: 'css-loader!stylus-loader?paths[]=node_modules',
fallback: 'vue-style-loader'
}),
less: vueExtractPlugin.extract({
use: 'css-loader!less-loader',
fallback: 'vue-style-loader'
}),
i18n: '#kazupon/vue-i18n-loader',
} : {
js: {
loader: 'babel-loader',
options: Config.babel()
},
i18n: '#kazupon/vue-i18n-loader',
},
postcss: Config.postCss,
preLoaders: Config.vue.preLoaders,
postLoaders: Config.vue.postLoaders,
esModule: Config.vue.esModule
}
},
// ...
]
},
// ...
});

How do I make Angular 2 final (2.0.0, after rc7) and TestBed work in this Plunkr?

I just want to initiate TestBed, so I can use TestBed functions. I am trying this in my src/test.spec.ts and it is not working:
TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
I also tried doing a System.import for TestBed and it didn't work. For example, this didn't work as a script tag in index.html.
Promise.all([
System.import('#angular/core/testing'),
System.import('#angular/platform-browser-dynamic/testing')
]).then(function (providers) {
var testing = providers[0];
var testingBrowser = providers[1];
testing.TestBed.initTestEnvironment(testingBrowser.BrowserDynamicTestingModule,
testingBrowser.platformBrowserDynamicTesting());
}).then(function() {
return Promise.all(
return System.import(app/test.spec.ts); //"app" stands for // './src' in the config.js file for SystemJS
);
})
Here is the Plunkr:
https://plnkr.co/edit/K0IyBnR8F4F7zOp6LETi?p=preview
Seems you forgot about rxjs and you need also to update zonejs and include some other zonejs scripts:
config.json
map: {
'app': './src',
'typescript': 'npm:typescript#1.8.0/lib/typescript.js',
'#angular/core': 'npm:#angular/core#2.0.0/bundles/core.umd.js',
'#angular/common': 'npm:#angular/common#2.0.0/bundles/common.umd.js',
'#angular/compiler': 'npm:#angular/compiler#2.0.0/bundles/compiler.umd.js',
'#angular/platform-browser': 'npm:#angular/platform-browser#2.0.0/bundles/platform-browser.umd.js',
'#angular/platform-browser-dynamic': 'npm:#angular/platform-browser-dynamic#2.0.0/bundles/platform-browser-dynamic.umd.js',
'#angular/http': 'npm:#angular/http#2.0.0/bundles/http.umd.js',
'#angular/router': 'npm:#angular/router#2.0.0/bundles/router.umd.js',
'#angular/forms': 'npm:#angular/forms#2.0.0/bundles/forms.umd.js',
'#angular/upgrade': 'npm:#angular/upgrade#2.0.0/bundles/upgrade.umd.js',
'#angular/core/testing': 'npm:#angular/core#2.0.0/bundles/core-testing.umd.js',
'#angular/common/testing': 'npm:#angular/common#2.0.0/bundles/common-testing.umd.js',
'#angular/compiler/testing': 'npm:#angular/compiler#2.0.0/bundles/compiler-testing.umd.js',
'#angular/platform-browser/testing': 'npm:#angular/platform-browser#2.0.0/bundles/platform-browser-testing.umd.js',
'#angular/platform-browser-dynamic/testing': 'npm:#angular/platform-browser-dynamic#2.0.0/bundles/platform-browser-dynamic-testing.umd.js',
'#angular/http/testing': 'npm:#angular/http#2.0.0/bundles/http-testing.umd.js',
'#angular/router/testing': 'npm:#angular/router#2.0.0/bundles/router-testing.umd.js',
'#angular/forms/testing': 'npm:#angular/forms#2.0.0/bundles/forms-testing.umd.js',
'rxjs': 'npm:rxjs'
},
index.html
<script src="https://unpkg.com/zone.js#0.6.25/dist/zone.js"></script>
<script src="https://unpkg.com/zone.js#0.6.25/dist/long-stack-trace-zone.js"></script>
<script src="https://unpkg.com/zone.js#0.6.25/dist/async-test.js"></script>
<script src="https://unpkg.com/zone.js#0.6.25/dist/fake-async-test.js"></script>
<script src="https://unpkg.com/zone.js#0.6.25/dist/sync-test.js"></script>
<script src="https://unpkg.com/zone.js#0.6.25/dist/proxy.js"></script>
<script src="https://unpkg.com/zone.js#0.6.25/dist/jasmine-patch.min.js"></script>
Besides that you have an error in your component:
src/component.ts
export class myCmp {
this._testVar = "initial value";
You should remove this.
Here's Plunker Example for your case. Hope it will help you!

TestRunner Not Running My Mocha / Chai Tests

Despite my best efforts, I can't seem to get my testRunner.html to acknowledge my tests when I run the testRunner.html page in the browser. I've confirmed that it pulls in the test files and runs through the expect but the test runner is still saying that zero passed and zero failed. I've also tried moving the mocha.run() command into the testRunner.html page as an inline script to no effect.
What have I configured incorrectly?
testRunner.html
<!DOCTYPE html>
<html lang = "en">
<head>
<meta charset = "utf-8" />
<title> Tests </title>
<link href = "../node_modules/mocha/mocha.css" rel = "stylesheet">
</head>
<body>
<div id="mocha"></div>
<script src="../node_modules/mocha/mocha.js"></script>
<script>
mocha.setup('bdd');
</script>
<script src = "../node_modules/requirejs/require.js" data-main = "test.config.js"></script>
</body>
</html>
test.config.js
require.config({
baseUrl: '../src/public/js',
paths: {
jquery: '//code.jquery.com/jquery-2.1.1.min',
chai: '/../../../node_modules/chai/chai',
underscore: '//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min',
backbone: '//cdnjs.cloudflare.com/ajax/libs/backbone.js/1.1.2/backbone-min',
marionette: 'http://marionettejs.com/downloads/backbone.marionette',
handlebars: '//cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.3.0/handlebars',
syphon: '//cdnjs.cloudflare.com/ajax/libs/backbone.syphon/0.4.1/backbone.syphon.min'
},
shim: {
underscore: {
exports: '_'
},
backbone: {
deps: ['jquery', 'underscore'],
exports: 'Backbone'
},
marionette: {
deps: ['backbone'],
exports: 'Marionette'
},
syphon: {
deps: ['backbone', 'marionette'],
exports: 'Backbone.Syphon'
},
handlebars: {
exports: 'Handlebars'
}
}
});
require([
'../../../test/src/appTest'
], function() {
if (typeof mochaPhantomJS !== "undefined") {
mochaPhantomJS.run();
}
else {
mocha.run();
}
});
appTest.js
define(['chai'], function(chai) {
describe('array', function() {
chai.expect(1+1).to.equal(2);
});
});
You need to put your test in an it call:
define(['chai'], function(chai) {
describe('array', function() {
it("1 + 1 = 2", function () {
chai.expect(1+1).to.equal(2);
});
});
});
This is wholly an issue with how you are using Mocha. RequireJS is not a factor at all here.