Add files to watch-list in karma preprocessor - javascript

Here is a repository created to demonstrate the issue:
brianmhunt/karma-rollup-preprocessor-issue-3
I'm trying to get karma-rollup-preprocessor working with Karma's builtin watch i.e. solve showpad/karma-rollup-preprocessor#3
In other words, in a preprocessor, I want to add files to Karma's watch list.
It's easy to get the list of files Rollup uses to compile. Rollup returns a list of files that it reads (ones one wants to watch), so in the preprocessor I am trying to add files to the list karma watches.
Basically I want to add this (or the working equivalent) to the preprocessor:
bundle.modules.forEach((module) => {
files.unshift({
pattern: module.id, /* The full file path, from Rollup */
watched: true,
included: false,
nocache: false,
served: false,
})
})
Where files is Karma's config.files or fileList or whatever place one needs to put the files being watched.
Doing the above with config.files, the files are indeed being added to the watcher, but .on(fileList.changeFile) fails the _isIncluded.
So it looks like the files also (or alternatively) must be added to the fileList.
Unfortunately when I try to add the fileList to the $inject, I get the error:
Error: Can not resolve circular dependency! (Resolving: preprocess -> preprocessor:rollup -> fileList -> preprocess).
I've looked at basically all the other preprocessors that look like they could also add includes, but I have found no indication of how to do it.
Is there a canonical way to add files Karma should watch from a preprocessor? Or otherwise how might one do this? This seems pretty clutch for a preprocessor in Karma, so it's surprising that it's not documented, apparent, or problematic in the other preprocessors.
EDIT Here's some more attempts:
I tried to add the watched patterns to the config.files in karma.conf i.e.
files: [
"spec/**/*.js",
{pattern:"src/**/*.js", included: false, watched: true}
]
But the src/* doesn't recompile when changed. The tests just re-run.
So I tried chokidar like this:
var server = new karma.Server(options)...
chokidar.watch("src/**/*.js")
.on('add', server.refreshFiles.bind(server))
.on('change', server.refreshFiles.bind(server))
I also tried it with a debounce, in case Karma was slower on the refresh, but it seems the tests won't re-run.
I rooted around karma-browserify for inspiration but it was a bit too convoluted to pick up without delving in.

I've issued a pull request to resolve this.
Until it is merged one can use my repo i.e. put "karma-rollup-preprocessor": "brianmhunt/karma-rollup-preprocessor" in package.json dependencies or devDependencies.
EDIT: Superceded by https://github.com/Kflash/karma-rollup-plugin

Related

Load Json Fixture in Karma/Mocha

I am using Karma with Mocha and karma-fixture. If I go into debug when I run tests, I can see the file is loaded in the server. If I changed the config included:true, then I can see it's loaded on the console. The extension is changed to .js (rather than .json) and if I view source on the file (in browser window) the json is wrapped in a function -- so it seems like everything is happening as described in the documentation. However, I get an error that the file cannot be found. I have included the relevant configs and errors below.
Update 1
I was able to load the fixture with require -- which I'm using anyway to manage and load dependencies. The data is assigned to an array like this: __json__['test/fixtures/json-data/querybrowser']. I would still be interested in knowing why I can't use the fixture.load() function. I feel like I am missing a simple detail here.
Thank you!
The configuration:
The file is located here, pathed from root of my project: \test\fixtures\json-data\querybrowser.json
Karma
files: [{pattern: 'test/fixtures/{,*/}*', watched: true, included: false, served: true}]
TEST spec
fixture.setBase('base/test/fixtures/json-data');
querybrowser_json = fixture.load('querybrowser.json');
The Error
Chrome 48.0.2564 (Windows 7 0.0.0) Query Browser Function Tests "before all" hook FAILED
ReferenceError: Cannot find fixture 'base/test/fixtures/json-data/querybrowser.js'
at Fixture._throwNoFixture (////node_modules/karma-fixture/lib/fixture.js:141:13)
Have you:
Made sure that you're including the JSON files in both your files array and your preprocessors array inside your karma config?
Made sure that you have transformPath property defined in jsonFixturesPreprocessor as per https://github.com/billtrik/karma-fixture/issues/10?
I had the same issue as you but doing these things fixed it for me.

Glob Syntax: Breadth first traversal to get all items in a folder using Karma

I'm writing Karma to test some AngularJS code. In order for the tests to run correctly, certain modules have to be run before others. Controllers and services come after the module instantiation.
The way my code is structured right now (grossly simplified) is I have an "app" folder which contains another folder called "controllers".
Inside "app", I have an app.js file. Inside "app/controllers", I have someController.js.
/root
--- /app
--- app.js
--- /controllers
--- someController.js
I'm using Karma to load all my files before I run tests. The tests are loaded after the source (app) files. But it's loading the source files in an order that's undesirable. It loads someController.js first before loading app.js. Probably because it does a depth-first traversal. I'm not looking for an algorithm or anything because that's all that Google seems to give me. Just a simple string glob pattern.
The syntax I'm using is "root/**/*.js" to get all the files in all sub-directories of "root".
How do I specify to Karma that I want to load all the files in "/app" before loading files in "app/controllers/"? (ie. breadth first traversal)
The example I'm following from Karma's documentation is here: http://karma-runner.github.io/0.13/plus/requirejs.html under the Configure Karma section.
Specifically:
files: [
{pattern: 'lib/**/*.js', included: false},
{pattern: 'src/**/*.js', included: false},
{pattern: 'test/**/*Spec.js', included: false},
'test/test-main.js'
],
Thanks!
I don't know of a way to explicitly control the traversal strategy of the globbing, but you can manually create multiple patterns which will force the desired order. If you use a simple dir/*.js pattern first, only the top level .js files will be matched, and then in a second pass you can go deeper with a more permissive dir/**/*.js pattern.
In your case, these patterns would be something like:
{pattern: 'app/*.js', included: false},
{pattern: 'app/**/*.js', included: false},
However, if you have several places where you need to specify the loading order, this can become cumbersome to maintain. For cases like this I found it very helpful to set up the build environment to generate the includes into the karma configuration file at the same time as generating the includes into the main .html file of the app - usually the same order restrictions apply to both. For example, if you're using something like grunt-wiredep, this can be as easy as adding a new target with a custom fileTypes entry to set up the proper syntax.

Does RequireJS provide a dependency api?

I would like to pass a requireJS config.json and a source file like my main.js and receive a list of all its dependencies.
This is probably part of the r.js optimization application however I couldn't find any documentation about its inner structure.
Does RequireJS provide such an api to generate a dependency list?
There is no API that I know of. However, if you run r.js (the optimizer) with a build configuration like this:
({
baseUrl: ...,
dir: ...,
mainConfigFile: ".../config.js",
findNestedDependencies: true,
name: "main",
optimize: "none"
})
then you'll get a build.txt file in the directory where you specify to store the optimized build (the value of dir). The general format of this file is:
<output-file1>
----------------
<module A>
<module B>
<output-file2>
----------------
<module C>
<module D>
This tells you that the output file output-file1 contains modules A and B, output-file2 contains modules C and D.
With the configuration I suggested above, you should have only one output file listed in the build.txt file for your main module, and the list of modules listed under it are all the modules it depends on.
In the configuration above I've set optimize: "none" because it would save time if the only thing you care about is dependencies. In a real build you'd want to let r.js use uglifyjs to minify your code. Also, findNestedDependencies: true is there to tell r.js to find dynamic calls to require in the middle of your code. And thins brings up a major warning. If you have this:
if (blah)
require(["foo"], function (foo) {...});
then findNestedDependencies: true will be able to detect that there is a dependency on the module foo. However, there is no way for r.js to handle this:
var module_name = obj[key];
require([module_name], function (module) {...});
To know what module this code is loading, r.js would have to execute the code (and even then there's a limit to what code execution can discover).

Excluding .svn folders when optimising with requirejs

I am attempting to optimise a project using r.js, with source code held in subversion. I'm getting the following error:
> node r.js -o generic-profile.js
> Error: Error: EPERM, operation not permitted 'C:\xxx\GUI\generic\.svn\entries'
at Object.fs.unlinkSync (fs.js:760:18)
I believe the problem is that r.js is attempting to copy the .svn directories from the source folders to the build folders.
Is there a way to exclude .svn directories when running r.js? Can I add something to my build profile, for example?
Here is what my build profile currently look like:
({
"appDir": "../src/generic/",
"baseUrl": ".",
"dir": "../generic/",
"include": ["../vendor/require",
"../vendor/text",
"../vendor/i18n"],
"optimize": "uglify2",
"modules": [
{
name: "app"
}
],
"mainConfigFile": "generic-config.js"
})
Update
Since reading Louis's excellent answer, it's clear that this is not an issue with subversion, but a poorly-configured build profile. I've done some further reading on how to set up a build profile, and this example helped immensely.
If you're having a similar problem, this may help.
By default r.js already excludes from processing directories that begin with a period. The setting is fileExclusionRegExp and the default value is /^\./ so .svn will be skipped. (Looking at the code of r.js I see that fileExclusionRegExp matches against the basename of each file.)
The error you are seeing is consistent with your input directory coinciding with your output. You do not set keepBuildDir to true, so your output directory is being removed. Change your dir to build somewhere else.
Or you keep your output under version control. Setting keepBuildDir to true would take care of the immediate problem but could create more problems down the road if you perform transformations on the output of r.js.

TypeScript: How do you test your client-side code?

When I write tests for my in-browser TS code, I hit the following problem. My "test" code files are located in a separate folder from the "application" code files (an arrangement that I am not willing to give up). Therefore, in order to import my "app" modules, I have to do this:
// tests/TS/SubComponent/Module.Test.ts
import m = module("../../Web/Scripts/SubComponent/Module");
This compiles just fine. But when loaded in browser, it will obviously not work, because from the standpoint of RequireJS running in the browser, the module is located at "app/SubComponent/Module" (after being remapped through web server and RequireJS config).
With TS 0.8.3 I was able to pull off this clever trick, but in 0.9.0 it no longer works, because now the compiler doesn't let me treat a module as an interface.
So the question is: how do you test your client-side code?
Clearly, I can't be the only person to be doing it, can I? :-)
I can't tell if you are using Visual Studio - this next bit is Visual Studio specific...
This is how I do it:
In my test project, I created a folder named "ReferencedScripts" and
referenced the scripts from the project being tested (add existing
item > add as link). Set the file to copy to the output folder.
Source: Include JavaScript and TypeScript tests in Visual Studio.
Using add-as-link makes the scripts available in your test project.
Not using Visual Studio? I recommend creating a task / job / batch file to copy the files into the test folder. You could even use tsc to do this task for you.
I am in the middle of a project where I have to migrate parts of a large javascript project to typescript and this is how I managed to keep the tests running:
Use grunt-typescript task to watch and compile all my .ts files from the source to a tmp folder (with their source-maps). If you only have to deal with typescript files, then you can use the tsc in watch mode to do it as well. The latter would be faster, but the former allowed me to simultaneous edit javascript and typescript files with livereload.
Include the .ts files in karma.conf but don't watch them or include them:
// list of files / patterns to load in the browser
files = [
JASMINE,
JASMINE_ADAPTER,
// ...
// We want the *.js to appear in in the window.__karma__.files list
{ pattern: 'app/**/*.ts', included: false, watched: false, served: true },
{ pattern: 'app/**/*.js', included: false },
// We do watch the folder where the typescript files are compiled
{ pattern: 'tmp/**/*.js', included: false },
// ...
// Finally, the test-main file.
'tests/test-main.js'
];
Finally, in the test-main.js file, I mangle the names of typescript files and declare them as require modules with the correct paths (to the corresponding .js file) in test-main.js:
var dynPaths = {
'jquery' : 'lib/jquery.min',
'text' : 'lib/text'
};
var baseUrl = 'base/app/',
compilePathUrl = '../tmp/';
Object.keys(window.__karma__.files)
.forEach(function (file) {
if ((/\.ts$/).test(file)) {
// For a typescript file, include compiled file's path
var fileName = file.match(/(.*)\.ts$/)[1].substr(1),
moduleName = fileName.substr(baseUrl.length);
dynPaths[moduleName] = compilePathUrl +
fileName.substr(baseUrl.length);
}
});
require({
// Karma serves files from '/base'
baseUrl: '/' + baseUrl,
paths: dynPaths,
shim: { /* ... */ },
deps: [ /* tests */ ],
// start test run, once requirejs is done
callback: function () {
window.__karma__.start();
}
});
Then as I edit the typescript files, they are compiled and put in the tmp folder as javascript files. These trigger karma's auto watch and it reruns the tests. In the tests, the require calls resolve correctly since we have explicitly overwritten the paths to the typescript files.
I realise that this is a bit hacky, but I had to jump through similar hoops while trying to include all my tests with REQUIRE_ADAPTER. So I assumed that there is no cleaner way of doing it.
Hopefully, if typescript becomes more prevalent, we will see better support for testing.
So here's ultimately what I've done: it turns out that Karma can handle/watch/serve files that are not within the base directory, and it makes them look to the browser in the form of "/absolute/C:/dir/folder/blah/file.js". This happens whenever files -> pattern starts with "../".
This feature can be used to make RequireJS see the whole directory structure exactly as it exists on the file system, thus allowing the tests to import app modules in the form of "../../Web/App/Module.ts".
files = [
// App files:
{ pattern: '../../Web/App/**/*', watched: true, served: true, included: false },
// Test files:
{ pattern: '../js/test/**/*.js', watched: true, served: true, included: false }
];
Reference (version 0.8): http://karma-runner.github.io/0.8/config/files.html
Since the typescript code is compiled to Javascript you can use all Javascript test frameworks.
I am using Jasmine: https://github.com/pivotal/jasmine/wiki
You can write your tests in Typescript with the .d.ts file here: https://github.com/borisyankov/DefinitelyTyped/blob/master/jasmine/jasmine.d.ts
But my client code is rather small and compiled to one output file, so I don't have the module issues that you describe.
Might be that I misunderstood your question - can't comment yet...
The runtime of the browser does not need any typescript information. So your test script should import the compiled ts files the same way as any other javascript files they need. Might be that you have to copy them to a subfolder of your test-project before you run your script.
I assume the bigger problem is that you have no interface information. Why do you want to import these informations instead of referencing them? Especially since importing them will also occur in the browser.
The Reference will only take place in the IDE , so it does not matter in which folders the interface-files are located.
/// <reference path="../../Web/Scripts/SubComponent/Module/References.ts" />

Categories

Resources