У меня довольно простое клиентское приложение javascript, оно не использует никаких фреймворков, просто шаблон jquery и модуля.Он поддерживается простым экспресс-сервером, который просто включает клиентское приложение js в теги скрипта на каждой странице.
Вот соответствующие части приложения:
// public/js/lib/namespace.js
const App = {
pages: {},
state: {},
routes: {}
};
// public/js/lib/routes.js
App.routes = (function(app) {
const mod = {
init: function(options) {
options = options || {};
this.bindRoutes();
},
bindRoutes: function() {
app.state.router.add('login', () => app.pages.login.init());
}
};
return mod;
})(App);
// public/js/lib/pages/login.js
App.pages.login = (function($, app) {
const mod = {
init: function(opts) {
const options = opts || {};
// tests should run the code that is here, but code it's never reached
// ...
}
};
return mod;
})(jQuery, App);
// public/js/lib/app.js
App.main = (function($, window, app) {
const l = window.location;
app.state.location = `${l.pathname}${l.search}${l.hash}`;
app.state.router = new Router({
mode: 'history',
page404: function (path) {}
});
const mod = {
init: function(options) { // When this runs window.location is set to debug.html
options = options || {};
app.routes.init();
app.state.router.addUriListener();
app.state.router.navigateTo(app.state.location);
}
}
return mod;
})(jQuery, window, App);
Шаблон ejs-сервера Express:
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<link rel='stylesheet' type="text/css" href='css/style.css' />
</head>
<body>
<h1><%= title %></h1>
<p>Welcome to <%= title %></p>
<script src="js/vendor/jquery/dist/jquery.min.js"></script>
<script src="js/vendor/vanilla-router/dist/vanilla-router.js"></script>
<script src="js/namespace.js"></script>
<script src="js/lib/routes.js"></script>
<script src="js/lib/pages/login.js"></script>
<script src="js/lib/app.js"></script>
<script type="text/javascript">
$(function() { App.main.init(); });
</script>
</body>
</html>
// karma.conf.js
const vendorFileGlob = 'public/js/vendor/**/dist/*.min.js';
const testFileGlob = 'test/client/**/*.test.js';
const appFiles = [
'public/js/lib/namespace.js',
'public/js/lib/routes.js',
'public/js/lib/pages/login.js',
'public/js/lib/app.js',
]
module.exports = function setKarmaConfig(config) {
config.set({
basePath: '',
frameworks: [ 'mocha', 'chai', 'sinon' ],
files: [
// 'test/client/util/globals.js', // doesn't make any difference to window.location
vendorFileGlob,
...appFiles,
testFileGlob
],
exclude: [],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: false,
browsers: [ 'Chrome', 'Firefox', 'Safari', 'Opera' ],
singleRun: true,
concurrency: Infinity
});
};
Полная структура файла приложения:
.
├── index.js
├── karma.conf.js
├── lib
│ └── views
│ └── layout.ejs
├── package-lock.json
├── package.json
├── public
│ ├── css
│ │ └── style.css
│ ├── favicon.ico
│ └── js
│ ├── lib
│ │ ├── app.js
│ │ ├── namespace.js
│ │ ├── pages
│ │ │ └── login.js
│ │ └── routes.js
│ └── vendor
│ ├── jquery
│ └── vanilla-router
└── test
└── client
├── app.test.js
├── lib
│ └── pages
│ └── login.test.js
└── util
└── globals.js
Тесты:
// test/client/util/globals.js
window = {
location: {
hash: "",
pathname: "/login",
search: ""
}
}
// test/client/lib/app.test.js
describe('the environment', function() {
it('works,hopefully', function() {
expect(true).to.be.true;
});
it('should have a window object', function() {
expect(typeof window === 'object').to.be.true;
});
});
describe('the app', function() {
it('exists', function() {
expect(App).to.be.an('object');
});
});
it('should have the correct app basic structure', function() {
expect(Object.keys(App).length).to.be.equal(4);
expect(App.pages).to.be.an('object');
expect(App.main).to.be.an('object');
expect(App.routes).to.be.an('object');
expect(App.state).to.be.an('object');
});
// test/client/lib/pages/login.test.js
describe('#login', function() {
it('should run login page module', function() {
const loginSpy = sinon.spy(App.pages.login, 'init');
App.main.init();
expect(window.location.pathname).to.be.equal('/login');
expect(loginSpy.calledOnce).to.be.true;
});
});
Этот последний тестовый файл завершается с ошибкой:
expected '/context.html' to equal '/login'
Я запускаю тесты с помощью сценариев npm:
// scripts part of package.json
"scripts": {
"postinstall": "mkdir -p public/js/vendor; cp -r node_modules/{jquery,vanilla-router} public/js/vendor/",
"start": "node index.js",
"test": "karma start 2> /dev/null"
},
Проблема в том, что код в public / js / lib / pages/login.js никогда не достигается во время теста, потому что он выполняется только тогда, когда маршрутизатор получает значение window.location, равное «/ login», но при выполнении тестов всегда устанавливается значение debug.html или context.html.
Я попытался добавить файл global.js в массив файлов опций кармы, который устанавливает окно в ложную версию, но это не имеет никакого эффекта.
Как настроить window.location в тестах, чтобыкод клиентских страниц javascript выполняется?Возможно, есть какой-то другой способ проверить этот код?
[Обновление 1: добавлен шаблон ejs для экспресс-сервера]
[Обновление 2: добавлен karma.conf.js]
[Обновление 3: добавлена часть сценариев package.json]
[Обновление 4: добавлена проверка существования объекта окна]