I'm trying to get 100% code coverage with flow but I'm stuck with anotating my ServiceWorker. It is not throwing any errors, but all self.addEventListener(...) are marked as uncovered. For example in:
self.addEventListener("install", (event) => {
console.log("Event: Install");
event.waitUntil(
//Open the cache
caches
.open(cacheName)
.then((cache) => {
//Adding the files to cache
return cache.addAll(filesToCache).then(() => {
console.log("All files are cached.");
return self.skipWaiting(); //To forces the waiting service worker to become the active service worker
});
})
.catch((err) => {
console.log(err);
})
);
});
Even if I do something like let a = self flow will show this line as uncovered.
I tried changing self to this but that didn't help either :(
Does anyone have an idea how to fix this?
Related
I'm trying to implement a basic service worker to assure that users of my simple web app have the latest code. So when I update html, js, or css files I can increment the cachename in the service worker file and prompt users to refresh, clear their cache, and get the latest code.
Until now I've relied on hacky ways to update javascript files (including a parameter in the referring URL: /javascript-file.js?v=1).
The with the service worker code below seem unpredictable: sometimes small changes to JS or CSS are reflected after I increment the cachename (code below). Sometimes the changes are reflected without incrementing the cachename, which suggests the code is ALWAYS pulling from the network (wasting resources).
How can you troubleshoot which version of files the code is using and whether the service worker is using cached or network versions? Am I not understanding the basic model for using service workers to achieve this goal?
Any help appreciated.
serv-worker.js (in root):
console.log('Start serv-worker.js');
const cacheName = '3.2121';
var urlsToCache = [
'home.html',
'home-js.js',
'web-bg.js',
'css/main.css',
'css/edit-menus.css'
];
self.addEventListener('install', event => {
console.log('Install event...', urlsToCache);
event.waitUntil(
caches.open(cacheName)
.then(function(cache) {
console.log('Opened cache', cacheName);
return cache.addAll(urlsToCache);
})
);
});
// Network first.
self.addEventListener('fetch', (event) => {
// Check the cache first
// If it's not found, send the request to the network
// event.respondWith(
// caches.match(event.request).then(function (response) {
// return response || fetch(event.request).then(function (response) {
// return response;
// });
// })
// );
event.respondWith(async function() {
try {
console.log('aPull from network...', event.request);
return await fetch(event.request);
} catch (err) {
console.log('aPull from cache...', event.request);
return caches.match(event.request);
}
}());
});
self.addEventListener('message', function (event) {
console.log('ServiceWorker cache version: ', cacheName, event);
console.log('Received msg1: ', event.data);
if (event.data.action === 'skipWaiting') {
console.log('ccClearing cache: ', cacheName);
// caches.delete('1.9rt1'); // hardcode old one
// caches.delete(cacheName); // actually removes cached versions
caches.keys().then(function(names) {
for (let name of names)
caches.delete(name);
});
self.skipWaiting();
}
});
Code in web-bg.js, which home.html references:
function servWorker(){
let newWorker;
function showUpdateBar() {
console.log('Show the update mssgg...ddddd');
$('#flexModalHeader').html('AP just got better!');
$('#flexModalMsg').html("<p>AP just got better. Learn about <a href='https://11trees.com/support/release-notes-annotate-pro-web-editor/'>what changed</a>.<br><br>Hit Continue to refresh.</p>");
$('#flexModalBtn').html("<span id='updateAPbtn'>Continue</span>");
$('#flexModal').modal('show');
}
// The click event on the pop up notification
$(document).on('click', '#updateAPbtn', function (e) {
console.log('Clicked btn to refresh...');
newWorker.postMessage({ action: 'skipWaiting' });
});
if ('serviceWorker' in navigator) {
console.log('ServiceWORKER 1234');
navigator.serviceWorker.register(baseDomain + 'serv-worker.js').then(reg => {
console.log('In serviceWorker check...', reg);
reg.addEventListener('updatefound', () => {
console.log('A wild service worker has appeared in reg.installing!');
newWorker = reg.installing;
newWorker.addEventListener('statechange', () => {
// Has network.state changed?
console.log('SSState is now: ', newWorker.state);
switch (newWorker.state) {
case 'installed':
if (navigator.serviceWorker.controller) {
// new update available
console.log('Detected service worker update...show update...');
showUpdateBar();
}
// No update available
break;
}
});
});
});
let refreshing;
navigator.serviceWorker.addEventListener('controllerchange', function (e) {
console.log('a1111xxxListen for controllerchange...', e);''
if (refreshing) return;
console.log('Refresh the page...');
window.location.reload();
refreshing = true;
});
} // End serviceworker registration logic
return;
} // END serv-worker
You've commented out the section for /// Check the cache first and then below that the try/catch statement again pulls from the network and falls back to the cache.
Uncomment this section of code and see if you're loading from the cache first.
// event.respondWith(
// caches.match(event.request).then(function (response) {
// return response || fetch(event.request).then(function (response) {
// return response;
// });
// })
// );
Don't forget that even if you request from the network from the service worker the browser will still use it's own internal cache to serve data. How long the data stays in the browser's cache depends on the expiration headers being sent by the server.
When using expires, it's still a fairly common solution to do something like:
index.html - expires after an hour. Has script/css tags that call out file names with ?v=x.y.z
/resources - folder that holds js and css. This folder has a very long expiration time. But that long expiration is short circuited by changing the ?v=x.y.z in index.html
I've used the above successfully in Progressive Web Apps (PWAs). But it is a little painful when debugging. The best option here is to manually clear out the cache and service worker from Dev Tools \ Application, if you're in Chrome.
I've been trying to build a progressive Django Web App with a service worker and django-pwa. Everything seems to work fine, but whenever I load the page, turn on offline mode in Chrome, and reload, I get this error:
"The FetchEvent for "https://haverford.getdibs.app/login/?redirect_to=/dashboard/" resulted in a network error response: a redirected response was used for a request whose redirect mode is not "follow"".
Now I've seen that someone else came into a seemingly similar problem here, but I don't quite understand what's going on in the answer. I think I need to set the redirect mode to "follow" for a request that I make with my fetch, but I'm not sure how to do that.
Here is my service worker:
var staticCacheName = "django-pwa-v" + new Date().getTime();
var filesToCache = [
'/offline',
];
// Cache on install
self.addEventListener("install", event => {
this.skipWaiting();
event.waitUntil(
caches.open(staticCacheName)
.then(cache => {
return cache.addAll(filesToCache);
})
)
});
// Clear cache on activate
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames
.filter(cacheName => (cacheName.startsWith("django-pwa-")))
.filter(cacheName => (cacheName !== staticCacheName))
.map(cacheName => caches.delete(cacheName))
);
})
);
});
// Serve from Cache
self.addEventListener("fetch", event => {
event.respondWith(
caches.match(event.request)
.then(response => {
return response || fetch(event.request);
})
.catch(() => {
return caches.match('offline');
})
)
});
I'm under the impression that i somehow have to make the event.request's redirect mode to 'follow', but when I put a breakpoint in that line and examine the event.request object, its redirect is set to 'manual'. How would I change it?
Any help in my quest to understanding what the issue actually is would be much appreciated.
Thank you.
Thanks a lot, sideshowbarker.
Method 1 in this link helped solve my problem.
I replaced my install event from this:
self.addEventListener("install", event => {
this.skipWaiting();
event.waitUntil(
caches.open(staticCacheName)
.then(cache => {
return cache.addAll(filesToCache);
})
)
});
to this:
self.addEventListener("install", event => {
this.skipWaiting();
event.waitUntil(
caches.open(staticCacheName)
.then(cache => {
return fetch('/offline')
.then(response => cache.put('/offline', new Response(response.body)));
})
)
});
Of course, right now it only works for that one URL endpoint ('/offline'), but it would be similar for multiple fetch requests I suppose.
I implemented the service worker from pwabuilder.com and it works just fine
The problem is that the service worker runs even if the browser is online, so every js functions runs twice, one from the service worker and one from my other js files
Should I look if it's an active service worker before I run my js functions or should I somehow make sure that the service worker is not running when the browser is online?
This is the code I run in my main index file
if (navigator.serviceWorker.controller) {
//console.log('[PWA Builder] active service worker found, no need to register')
} else {
//Register the ServiceWorker
navigator.serviceWorker.register('pwabuilder-sw.js', {
scope: './'
}).then(function (reg) {
//console.log('Service worker has been registered for scope:' + reg.scope);
});
}
The pwabuilder-sw.js looks like this:
self.addEventListener('install', function (event) {
var indexPage = new Request('');
event.waitUntil(
fetch(indexPage).then(function (response) {
return caches.open('pwabuilder-offline').then(function (cache) {
//console.log('[PWA Builder] Cached index page during Install' + response.url);
return cache.put(indexPage, response);
});
}));
});
//If any fetch fails, it will look for the request in the cache and serve it from there first
self.addEventListener('fetch', function (event) {
var updateCache = function (request) {
return caches.open('pwabuilder-offline').then(function (cache) {
return fetch(request).then(function (response) {
//console.log('[PWA Builder] add page to offline' + response.url);
return cache.put(request, response);
});
});
};
event.waitUntil(updateCache(event.request));
event.respondWith(
fetch(event.request).catch(function (error) {
//Check to see if you have it in the cache
//Return response
//If not in the cache, then return error page
return caches.open('pwabuilder-offline').then(function (cache) {
return cache.match(event.request).then(function (matching) {
var report = !matching || matching.status === 404 ? Promise.reject('no-match') : matching;
return report;
});
});
})
);
});
Service Workers are meant to work all the time once registered, installed and activated
Service workers are event driven and their primary use is to act as a caching agent, to handle network requests and to store content for offline use. Secondly to handle push messaging.
I trust you understand that in order to act as a caching agent the service worker will run regardless if the application is online or offline. You have various caching scenarios to consider.
It is hard to provide exact solution for the mentioned: 'every js functions runs twice'.I doubt that all JS functions would always run twice. It seems this is implementation dependant.
Service workers cannot have a scope above their own path, by default it will control all resources below the scope of the service worker, this scope can also be restricted.
navigation.serviceWorker.register(
'/pwabuilder-sw.js', { //SW located at the root level here
scope: '/app/' //to control all resource accessed form within path /app/
}
);
I believe that the script from pwabuilder.com does attempt to cache all resources even resources that should not be cached such as POST requests. You may need to modify the caching policy depending on what type of resources your are using.
There is no simple solution here and no easy answer can be provided.
In general you can use the service worker to cache resources in one of the following ways:
Cache falling back to network
self.addEventListener('fetch', (event) => {
event.responseWith(
caches.match(event.request)
.then((response) => {
return response || fetch(event.request);
})
);
});
Network falling back to cache
//Good for resources that update frequently
//Bad for Intermittend and slow connections
self.addEventListener('fetch', (event) => {
event.responseWith(
fetch(event.request).catch(() => {
return caches.match(event.request);
})
);
});
Cache then network
//Cache then network(1) (send request to cache and network simultaneousely)
//show cached version first, then update the page when the network data arrives
var networkDataReceived = false;
var networkUpdate = fetch('/data.json')
.then((response) => {
return response.json();
}).then((data) => {
networkDataReceived = true;
updatePage(data);
});
//Cache then network(2)
caches.match('/data.json')
.then ((response) => {
return response.json();
}).then((data) => {
if (!networkDataReceived) {
updatePage(data);
}
}).catch(() => {
return networkUpdate;
});
Generic fallback
self.addEventListener('fetch', (event) => {
event.responseWith(
caches.match(event.request)
.then((response) => {
return response || fetch(event.request);
}).catch(() => {
return caches.match('offline.html');
})
)
});
I hope the above helps at least a little find the exact issue you are facing. Cheers and happy codding!
React added this React.strictMode HOC that runs twice certain parts of the application, like class component constructor, render, and shouldComponentUpdate methods.
Check the docs:
https://reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects.
See if you have <React.StrictMode> at the top of your index.js file and if so, you might want to run a test without it.
I'm trying some basic service workers. The service worker itself will work normally the first time the service worker is registered. The problem I always get is once the person revisits the website in the future (e.g. the following day) and tries to access a .htaccess/.htpasswd protected directory. Instead of getting the dialog box, as normal, they go straight to a 401 error.
This is how I am registering the service worker in script tags in the HTML.
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').then(function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
});
}
I have tried a couple of different methods in the sw.js itself and every time I get the same error. This is one from the Google airhorner example, I believe...
self.addEventListener('install', e => {
const timeStamp = Date.now();
e.waitUntil(
caches.open('somename').then(cache => {
return cache.addAll([
`/`,
`/index.html`,
`/css/tour-2.css`
])
.then(() => self.skipWaiting());
})
);
});
self.addEventListener('activate', event => {
event.waitUntil(self.clients.claim());
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request, {ignoreSearch: true}).then(response => {
return response || fetch(event.request);
})
);
});
Does anyone know if it is possible to use service workers with websites with .htaccess protected directories?
Thanks.
One way to cure is:
self.addEventListener('fetch', event => {
// Exclude admin panel.
if (0 === event.request.url.indexOf("https://www.my-site.com/my-protected-area")) {
return;
}
It should help.
Source: TIV.NET
I have integrated service worker in my website. Everything used to works perfectly, but now, I have an error when my service worker try to install :
Uncaught (in promise) TypeError: Request failed at anonymous service-worker.js:1
And my service worker is in the 'redundant' state.
I don't know why... I did not change my code, this is my index.html :
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js').then(function(reg) {
if(reg.installing) {
console.log('Service worker installing');
} else if(reg.waiting) {
console.log('Service worker installed');
} else if(reg.active) {
console.log('Service worker active');
}
}).catch(function(error) {
// registration failed
console.log('Registration failed with ' + error);
});
}
And here is my service-worker.js :
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open('v1').then(function(cache) {
return cache.addAll([
'/',
'/theme/website_mobile/js',
'/theme/website_mobile/css',
'/theme/website_mobile/js.js',
'/theme/website_mobile/css.css',
'/js/614cd8e.js',
'/css/f1407bb.css',
'/js/93779bc.js',
'/js/d228ec7.js',
'/theme/website_mobile/img/slider-home/slider1.jpg',
'/theme/website_mobile/img/slider-home/slider2.jpg',
'/theme/website_mobile/img/slider-home/slider3.jpg',
'/theme/website_mobile/img/slider-home/slider4.jpg',
'/theme/website_mobile/img/logo-website.png',
'/theme/website_mobile/img/picto-menu-close.png',
'/theme/website_mobile/img/picto-close.png',
'/var/website/storage/images/media/website-medias/website-materials/5163440-1-eng-GB/website-materials_article_list_main_website_enm.jpg',
'/theme/website_mobile/fonts/website-montserrat/Montserrat-Light.woff2',
'/theme/website_mobile/fonts/website-montserrat/Montserrat-Regular.woff2',
'/theme/website_mobile/fonts/website-montserrat/Montserrat-ExtraBold.woff2',
'/theme/website_mobile/fonts/website-avenir/Fonts/065a6b14-b2cc-446e-9428-271c570df0d9.woff2',
]);
})
);
});
self.addEventListener('fetch', function(event) {
event.respondWith(caches.match(event.request).then(function(response) {
// caches.match() always resolves
// but in case of success response will have value
if (response !== undefined) {
return response;
} else {
return fetch(event.request).then(function (response) {
// response may be used only once
// we need to save clone to put one copy in cache
// and serve second one
let responseClone = response.clone();
caches.open('v1').then(function (cache) {
cache.put(event.request, responseClone);
});
return response;
}).catch(function (e) {
return caches.match('/');
});
}
}));
});
A more stranger thing is when I tried to edit my service-worker.js, I added this :
self.addEventListener('install', function(event) {
**reg.update();**
which is a mistake, but I figured out that this works good ! I have an reg is undefined error in the console, but my service worker works good.
I tried to change the reg.update() part and put a simple console.log but, when I did that, the service-worker return in the redundant state and don't install...
I don't understand why when if I add an undefined object in the service worker code, it throw an error but it works great, and when I came back to my old code (which used to work before), it didn't install.
Maybe, I am doing it wrong somewhere... ?
Thanks
I just ran into this after pulling my hair out for several days.
In my case one of the URLs of the list was returning a 404, which made the entire Cache.addAll return promise reject with the extremely unspecific error TypeError: Request failed ("failed on what, why?!").
If your business logic allows for graceful caching, despite any one of the urls failing, you might want to change your approach to Cache.add of every item of the list:
var urls = ['/', ...]
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open('v1').then(function(cache) {
urls.forEach(function (url) {
cache.add(url).catch(/* optional error handling/logging */);
});
})
);
});