Hello i have a project that uses gulp for the build framework, and used karma with jasmine for the testing.
I am trying to integrate proxyquireify to mock the requires, i just added proxyquireify as browserify plugin in karma config, as i am using karma-browserify.
But this results in an error when running the tests, in the first line, saying 'require is undefined'.
What am i doing wrong?
here is my karma config
// Karma configuration
// Generated on Wed Nov 26 2014 17:57:28 GMT+0530 (IST)
module.exports = function(config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['browserify', 'jasmine'],
// list of files / patterns to load in the browser
files: [
'./components/renderer/**/*.spec.js',
'./components/tracker/**/*.spec.js'
],
// list of files to exclude
exclude: [
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'./components/**/*.spec.js' : [ 'browserify' ]
},
browserify: {
debug:true,
plugin: ['proxyquireify/plugin']
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['spec'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: false,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['Chrome'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false
});
};
proxyquireify works internally by substituting the require function provided by browserify.
In this case it seems the new substituted require function was not exposed to global scope.
I went through the code and found out proxyquireify creates the new require function in node_modules/proxyquireify/lib/prelude.js named as newRequire.
the issue i was having was that the newRequire function was not exposed in the global scope as the require function, so i changed node_modules/proxyquireify/lib/prelude.js so that
// Override the current require with this new one
return newRequire;
becomes
// Override the current require with this new one
require = newRequire;
and the newRequire was properly exposed to global scope and everything worked fine. Since this change is reset every time i do a npm install, i created a gulp task in my case which does this change every time before tests are run, i will add the gulp task for reference
// Task to modify proxyquireify so that it works properly, there is a bug in the npm library
gulp.task('_patch:proxyquireify', function() {
gulp.src(['./node_modules/proxyquireify/lib/prelude.js'])
.pipe(replace(/return newRequire;/g, 'require = newRequire;'))
.pipe(gulp.dest('./node_modules/proxyquireify/lib'));
});
I run this task before executing the test tasks, like this
// Task to run tests
gulp.task('run:test', ['_patch:proxyquireify'], function() {
//logic to run tests
};
I hope this helps, thanks
Related
I start using next-pwa and the basic setup worked like a charm. Now it want to play with the runtime caching option, which does not work for me:
My next.config.js includes the standard cache entries plus a custom one that should use the strategy StaleWhileRevalidate for each request going to /api/todoitem:
const withPWA = require("next-pwa");
const defaultRuntimeCaching = require("next-pwa/cache");
module.exports = withPWA({
reactStrictMode: true,
pwa: {
dest: "public",
disable: false, // disable PWA
register: true,
skipWaiting: true,
runtimeCaching: [
{
urlPattern: /\/api\/todoitem/,
method: "GET",
handler: "StaleWhileRevalidate",
options: {
cacheName: "todoApp-api",
expiration: {
maxEntries: 64,
maxAgeSeconds: 24 * 60 * 60, // 24 hours
},
},
},
...defaultRuntimeCaching,
],
},
});
Restart npm run dev fire up the browser -> fetch GET /api/todoitem -> and console.log tells me
workbox Network request for '/api/todoitem' returned a response with status '200'.
workbox Using NetworkOnly to respond to '/api/todoitem'
I tried a number of combinations of regexes, including defaultRuntimeCaching before or after my runtimeCache entry to no avail.
Any hints to get custom runtimeCache rules working would be greatly appreciated.
next.js 12.0.7
next-pwa 5.4.4
node.js v14.18.2
After some research I found:
In development mode, next-pwa creates a service worker that disables caching. It even tells me so on the console ;-):
[PWA] Build in development mode, cache and precache
are mostly disabled. This means offline support is
disabled, but you can continue developing other
functions in service worker.
When building the app via next build it creates a service worker that uses my custom rules and when starting the app next start the rules seem to work.
A bit hard to debug, so I tried to set mode: "production" on my developer machine but then for some reason the service worker gets rebuilt at every other request which brings the app to a grinding halt.
The application cache is now deprecated and browsers like Chrome are removing support.
We have an application that can work 100% offline while storing data in the indexeddb and syncing later when the user is back online. We need to transition this site from using application cache to service worker. We will be using Workbox for our service worker.
There are three main sections of our cache manifest that we must covert.
CACHE Section
This is a list of assets to precache. This is probably the most straight forward to transition as we are using workbox to precache these files.
NETWORK Section
We are using * here (probably most common) so that's probably not going to be an issue.
FALLBACK Section
We have quite a few entries in the fallback section. Basically they are redirect to the login page and are there in case someone refreshes the page offline.
Example:
FALLBACK:
/search /login
/customer-edit /login
/foo-bar-baz /login
...
My question:
Is there either 1) a general guide to converting application cache/cache manifest to service workers or 2) some specific guidance for converting the FALLBACK section to the equivalent functionality in a service worker.
Google and Duck Duck Go have not been extremely helpful.
There are existing projects to upgrade the app cache to service workers but most appear very beta, example from Google Chrome Labs: github.com/GoogleChromeLabs/sw-appcache-behavior
This is the solution I came up with using Google's workbox.
Sidenote: Workbox appears to have a has a solution for most common service worker use cases and has a very flexible distribution model that makes it quite easy to work with, either in a vanilla js environment or with your framework of choice.
We ended up converting our server side AppCache (cach manifest code) to generate a service worker. (How to precaching an url list coming from a json file?)
Depending on your server side language you're code will vary, but this is the end product that worked for us:
service-worker.js (generated server side)
const productVersion = "3.01";
importScripts('/assets/workbox/workbox-sw.js');
workbox.setConfig({
modulePathPrefix: '/assets/workbox/'
});
const { precacheAndRoute, createHandlerBoundToURL } = workbox.precaching;
const { NavigationRoute, registerRoute, setCatchHandler } = workbox.routing;
precacheAndRoute([
// cache index html
{url: '/', revision: '3.01' },
// web workers
{url: '/assets/some-worker.js?ver=3.01', revision: '' },
{url: '/assets/other-worker.js?ver=3.01', revision: '' },
// other js files
{url: '/assets/shared-function.js', revision: '3.01' },
// ... removed for brevity
// css
{url: '/assets/site.css', revision: '3.01' },
{url: '/assets/fonts/fonts.css', revision: '3.01' },
// svg's
{url: '/assets/images/icon.svg', revision: '3.01' },
{url: '/assets/images/icon-2.svg', revision: '3.01' },
// png's
{url: '/assets/images/img-1.png', revision: '3.01' },
{url: '/assets/images/favicon/apple-touch-icon-114x114.png', revision: '3.01' },
// ...
// ...
// fonts
{url: '/assets/fonts/lato-bla-webfont.eot', revision: '3.01' },
{url: '/assets/fonts/lato-bla-webfont.ttf', revision: '3.01' },
// sounds
{url: '/assets/sounds/keypress.ogg', revision: '3.01' },
{url: '/assets/sounds/sale.ogg', revision: '3.01' },
]);
// Routing for SPA
// This assumes DEFAULT_URL has been precached.
const DEFAULT_URL = '/';
const handler = createHandlerBoundToURL(DEFAULT_URL);
const navigationRoute = new NavigationRoute(handler, {
denylist: [
new RegExp('/ping'),
new RegExp('/upgrade'),
new RegExp('/cache.manifest'),
],
});
registerRoute(navigationRoute);
// This allows the main window to signal the service worker that
// it should go ahead and install if it's waiting.
addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
skipWaiting();
}
});
There's a few other things to note. We had to figure out how to smoothly upgrade from App?Cache to Service Workers. Turns out the generating an empty cache manifest for us did the trick.
We already had an upgrade process in place (prompt user to upgrade or force automatic upgrade with a count down timer) so we had to do some work to get that to work with service workers. Notice the end of the service worker file has addEventListener code. We actually call that from an upgrade page to get a smooth upgrade process. It goes something like this:
A) Upgrade script detects new version is available (many ways to do this, api call polling, etc)
B) If user accepts or timer expires redirect user to an upgrade page. This step is vital b/c you can't update a service worker with an app still running. So navigate to the upgrade page, wait for the service worker to get installed then tell it to skip waiting and the redirect to main (login) screen.
C) User is happily running new version of app.
Upgrade page code:
(this is a good page to show an "updating" UI of some type)
<script type="module">
import { Workbox } from '/assets/workbox/workbox-window.prod.mjs';
if ('serviceWorker' in navigator) {
const wb = new Workbox('/serviceworker');
// This code exists b/c a service worker can't update with just a refresh/reload in the
// browser. This is b/c on a reload, the old and new page exist simultaneously and the old MUST
// unload before the new service worker can automatically assume control. Also if multiple pages
// are open, this blocks the service worker from taking control (multiple pages should not an issue with this app).
// This code activates a waiting service worker and _then_ redirects back to the app.
// Add an event listener to detect when the registered
// service worker has installed but is waiting to activate.
wb.addEventListener('waiting', (event) => {
// Set up a listener that will reload the page as soon as the previously waiting
// service worker has taken control.
wb.addEventListener('controlling', (event) => {
window.location.replace('/login');
});
// Send a message telling the service worker to skip waiting.
// This will trigger the `controlling` event handler above.
wb.messageSW({type: 'SKIP_WAITING' });
});
wb.register();
}
// set a timeout in case the service worker has already installed.
setTimeout(function () {
window.location.replace('/login');
}, 30000);
</script>
Main page (index.html, etc)
(Handles if the user is coming to the app with a server worker ready to activate, so it needs a refresh to get the right assets/code loaded)
<script type="module">
import { Workbox } from '/assets/workbox/workbox-window.prod.mjs';
if ('serviceWorker' in navigator) {
const wb = new Workbox('/serviceworker');
wb.addEventListener('activated', (event) => {
// `event.isUpdate` will be true if another version of the service
// worker was controlling the page when this version was registered.
if (!event.isUpdate) {
// service worker was updated and activated for the first time.
// If your service worker is configured to precache assets, those
// assets should all be available now.
// this will only happen if the browser was closed when a new version was made available
// and it will only happen once per service worker install.
// Reload to so all libs are correct version.
window.location.reload(true);
}
});
wb.register();
}
</script>
I use gulp-notify to trigger notifications when tasks complete. If a task is ran standalone, a notification for that specific task is triggered. If a task is ran as a dependency of another task, a notification for all dependencies is triggered.
In gulp#3, I check if the task is being called as a dependency using gulp.seq, which contains an array of the tasks being ran. Let's say I have three tasks: default, styles, and scripts, with the later two set as dependencies of the first. When running gulp styles, gulp.seq will contain [ 'styles' ]. When running gulp (the default task), gulp.seq will contain [ 'styles', 'scripts', 'default' ]. Knowing that, I then check gulp.seq.indexOf("styles") > gulp.seq.indexOf("default"), which tells me weather or not styles was ran as part of the default task.
With gulp#4, it appears that gulp.seq no longer exists. I've tried digging through the documentation and source code with no luck. It seems like gulp.tree({ deep:true }) (docs) might be what I'm looking for, but I don't see anything in it that returns anything useful.
Is there an equivalent of gulp.seq in gulp#4?
The API gulp.seq was never an official prop exposed by Gulp. With Gulp 4, you cannot do that. gulp.tree({ /* */ }) will not solve this problem for you.
Having said that, if you still need to find whether a task has run during some other task's pipeline, then you will have to decorate every gulp task with your own wrapper using something like this:
let runTasks = [];
function taskWrapper(taskName, tasks, thisTask) {
let callbackTask;
function innerCallback(cb) {
runTasks.push(taskName);
cb();
}
if (thisTask) {
callbackTask = function(cb) {
thisTask(function () {
innerCallback(cb);
});
}
} else {
callbackTask = innerCallback;
}
const newTasks = [ ...tasks, callbackTask ];
gulp.task(taskName, gulp.series(newTasks));
}
// INSTEAD OF THIS
// gulp.task('default', gulp.series('style', 'script', function () { }));
// DO THIS
taskWrapper('default', ['style', 'script'], function(cb) {
console.log('default task starting');
cb();
});
NOTE: Above code snippets has limitation. If you use watch mode, array maintaining the executed tasks i.e. runTasks will keep on growing. Also, it assumes tasks will always run in series. For a parallel mode, the logic gets little complicated.
Finally, you can also have a predefault task to help it further:
taskWrapper('predefault', [], function(cb) {
// RESET runTasks
runTasks = [];
cb();
});
taskWrapper('default', ['predefault', 'style', 'script'], function(cb) {
console.log('default task starting');
cb();
});
Also, I am doubtful if gulp-notify will work with Gulp 4.
Through a bit of luck, I discovered this was possible via the module yargs, which I already have installed.
When running gulp styles, for example, I can check argv._.indexOf("styles") > -1, as it contains ['styles']. When running gulp (i.e the default task), it contains []. In my testing, this works perfectly for my use case.
This must be obvious but I can't find it. I want to preprocess my stylus/coffee files with a watcher in the dev environment and in production with a build task (isn't that common to all of us?) and also run a few more minification and uglification steps in production but I want to share the pipe steps common to both dev and production for DRY
The problem is that when I run the task which watches the files, the task which preprocesses does that to all the files since it has its own gulp.src statement which includes all stylus files.
How do I avoid compiling all files on watching while still keeping the compile task separate. Thanks
paths = {
jade: ['www/**/*.jade']
};
gulp.task('jade', function() {
return gulp.src(paths.jade).pipe(jade({
pretty: true
})).pipe(gulp.dest('www/')).pipe(browserSync.stream());
});
gulp.task('serve', ['jade', 'coffee'], function() {
browserSync.init({
server: './www'
});
watch(paths.jade, function() {
return gulp.start(['jade']);
});
return gulp.watch('www/**/*.coffee', ['coffee']);
});
One important thing in Gulp is not to duplicate pipelines. If you want to process your stylus files, it has to be the one and only stylus pipe. If you want to execute different steps in your pipe however, you have multiple choices. One that I would suggest would be a noop() function in conjunction with a selection function:
var through = require('through2'); // Gulp's stream engine
/** creates an empty pipeline step **/
function noop() {
return through.obj();
}
/** the isProd variable denotes if we are in
production mode. If so, we execute the task.
If not, we pass it through an empty step
**/
function prod(task) {
if(isProd) {
return task;
} else {
return noop();
}
}
gulp.task('stylus', function() {
return gulp.src(path.styles)
.pipe(stylus())
.pipe(prod(minifyCss())) // We just minify in production mode
.pipe(gulp.dest(path.whatever))
})
As for the incremental builds (building just the changed files with every iteration), the best way would be to get on the gulp-cached plugin:
var cached = require('gulp-cached');
gulp.task('stylus', function() {
return gulp.src(path.styles)
.pipe(cached('styles')) // we just pass through the files that have changed
.pipe(stylus())
.pipe(prod(minifyCss()))
.pipe(gulp.dest(path.whatever))
})
This plugin will check if the contents have changed with each iteration you have done.
I spend a whole chapter on Gulp for different environments in my book, and I found those to be the most suitable ones. For more information on incremental builds, you can also check on my article on that (includes Gulp4): http://fettblog.eu/gulp-4-incremental-builds/
I'm probably trying to make gulp do something that's not idiomatic, but here goes.
I want my build task to only run if the source files are newer than the output file.
In gulp, it seems standard practice to create a build task that always runs, and then set up a watch task to only run that build task when certain files change. That's okay, but it means that you always build on the first run.
So, is it possible to do what I want? Here's what I've got so far (newer is gulp-newer):
gulp.task('build_lib', function() {
return gulp.src(["app/**/*.ts"])
.pipe(newer("out/outputLib.js")) //are any of these files newer than the output?
** NEED SOMETHING HERE **
how do I say, "If I got _any_ files from the step before, replace all of them with a single hardcoded file "app/scripts/LibSource.ts" "?
.pipe(typescript({
declaration: true,
sourcemap: true,
emitError: false,
safe: true,
target: "ES5",
out: "outputLib.js"
}))
.pipe(gulp.dest('out/'))
});
I tried using gulpif, but it doesn't seem to work if there are no files going into it to begin with.
.pipe(gulpif(are_there_any_files_at_all,
gulp.src(["app/scripts/LibSource.ts"])))
However, my condition function isn't even called because there are no files on which to call it. gulpif calls the truthy stream in this case, so LibSource gets added to my stream, which isn't what I want.
Maybe doing all of this in a single stream really isn't the right call, since the only reason I'm passing those files through the "gulp-newer" filter is to see if any of them is newer. I'm then discarding them and replacing them with another file. My question still stands though.
You can write your own through/transform stream to handle the condition like so:
// Additional core libs needed below
var path = require('path');
var fs = require('fs');
// Additional npm libs
var newer = require('gulp-newer');
var through = require('through');
var File = require('vinyl');
gulp.task('build_lib', function() {
return gulp.src(["app/**/*.ts"])
.pipe(newer("out/outputLib.js"))
.pipe(through(function(file) {
// If any files get through newer, just return the one entry
var libsrcpath = path.resolve('app', 'scripts', 'LibSource.ts');
// Pass libsrc through the stream
this.queue(new File({
base: path.dirname(libsrcpath),
path: libsrcpath,
contents: new Buffer(fs.readFileSync(libsrcpath))
}));
// Then end this stream by passing null to queue
// this will ignore any other additional files
this.queue(null);
}))
.pipe(typescript({
declaration: true,
sourcemap: true,
emitError: true,
safe: true,
target: "ES5",
out: "outputLib.js"
}))
.pipe(gulp.dest('out/'));
});
I know like, this question was posted over 4 years ago, however; I am sure this problem crosses the path of everyone, and although I think I understand the question that is being asked, I feel that there is an easier way to perform this task, off which, I posted a similar question recently on stackoverflow at New to GULP - Is it necessary to copy all files from src directory to dist directory for a project?
It uses gulp-changed, and for me, it worked like a charm, so for others who may look at this post for similar reasons, have a look at my post and see if it is what you are looking for.
Kind Regards
You don't need to build first. You can on your 'first run' only run the watch task from which you run all the other ones.
example:
// Create your 'watch' task
gulp.task( 'watch', function() {
gulp.watch( 'scripts/*.js', [ 'lint', 'test', 'scripts' ] );
gulp.watch( 'styles/sass/*.scss', [ 'sass_dev' ] );
} );
// On your first run you will only call the watch task
gulp.task( 'default', [ 'watch' ] );
This will avoid running any task on startup. I hope this will help you out.
May I suggest gulp-newy in which you can manipulate the path and filename in your own function. Then, just use the function as the callback to the newy(). This gives you complete control of the files you would like to compare.
This will allow 1:1 or many to 1 compares.
newy(function(projectDir, srcFile, absSrcFile) {
// do whatever you want to here.
// construct your absolute path, change filename suffix, etc.
// then return /foo/bar/filename.suffix as the file to compare against
}