From e1694460db470aff2c75bade7da4a81241a35b66 Mon Sep 17 00:00:00 2001 From: Martijn Dwars Date: Tue, 7 Jul 2020 14:36:43 +0200 Subject: [PATCH 1/8] Add testcafe test --- build.gradle | 4 +- .../webpush/testcafe/TestCafeTest.java | 19 +++ .../webpush/testcafe/Webserver.java | 82 ++++++++++ src/test/resources/static/index.html | 20 +++ src/test/resources/static/push.js | 145 ++++++++++++++++++ src/test/resources/static/sw.js | 51 ++++++ 6 files changed, 320 insertions(+), 1 deletion(-) create mode 100644 src/test/java/nl/martijndwars/webpush/testcafe/TestCafeTest.java create mode 100644 src/test/java/nl/martijndwars/webpush/testcafe/Webserver.java create mode 100644 src/test/resources/static/index.html create mode 100644 src/test/resources/static/push.js create mode 100644 src/test/resources/static/sw.js diff --git a/build.gradle b/build.gradle index 737f443..9493098 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,6 @@ group 'nl.martijndwars' version '5.1.1-SNAPSHOT' repositories { - mavenLocal() mavenCentral() } @@ -52,6 +51,9 @@ dependencies { // For verifying Base64Encoder results in unit tests testCompile group: 'com.google.guava', name: 'guava', version: '27.0.1-jre' + + // To run a local webserver in the TestCafe tests + testCompile group: 'io.undertow', name: 'undertow-servlet', version: '2.1.3.Final' } wrapper { diff --git a/src/test/java/nl/martijndwars/webpush/testcafe/TestCafeTest.java b/src/test/java/nl/martijndwars/webpush/testcafe/TestCafeTest.java new file mode 100644 index 0000000..d7045b2 --- /dev/null +++ b/src/test/java/nl/martijndwars/webpush/testcafe/TestCafeTest.java @@ -0,0 +1,19 @@ +package nl.martijndwars.webpush.testcafe; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class TestCafeTest { + public static Webserver webserver; + + @BeforeAll + public static void startWebserver() { + webserver = new Webserver(); + webserver.start(); + } + + @Test + public void testEndToEnd() throws InterruptedException { + Thread.sleep(60 * 1000); + } +} diff --git a/src/test/java/nl/martijndwars/webpush/testcafe/Webserver.java b/src/test/java/nl/martijndwars/webpush/testcafe/Webserver.java new file mode 100644 index 0000000..e6381b2 --- /dev/null +++ b/src/test/java/nl/martijndwars/webpush/testcafe/Webserver.java @@ -0,0 +1,82 @@ +package nl.martijndwars.webpush.testcafe; + +import java.nio.file.Paths; +import java.security.Security; + +import com.google.gson.Gson; +import io.undertow.Handlers; +import io.undertow.Undertow; +import io.undertow.predicate.Predicates; +import io.undertow.server.HttpHandler; +import io.undertow.server.HttpServerExchange; +import io.undertow.server.handlers.form.FormData; +import io.undertow.server.handlers.form.FormDataParser; +import io.undertow.server.handlers.form.FormParserFactory; +import io.undertow.server.handlers.resource.PathResourceManager; +import nl.martijndwars.webpush.Notification; +import nl.martijndwars.webpush.PushService; +import nl.martijndwars.webpush.Subscription; +import org.apache.http.HttpResponse; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +public class Webserver { + protected final Undertow server; + + public static void main(String[] args) throws InterruptedException { + Webserver webserver = new Webserver(); + webserver.start(); + + Thread.sleep(600 * 1000); + } + + public Webserver() { + this.server = Undertow.builder() + .addHttpListener(5000, "localhost") + .setHandler(getHandler()) + .build(); + } + + public void start() { + server.start(); + } + + protected HttpHandler getHandler() { + return Handlers.predicate( + Predicates.suffixes(".html", ".js"), + Handlers.resource(new PathResourceManager(Paths.get("src/test/resources/static"))), + Handlers + .path(Handlers.redirect("/index.html")) + .addPrefixPath("/send", getSendHandler()) + ); + } + + protected HttpHandler getSendHandler() { + return exchange -> { + FormDataParser parser = FormParserFactory.builder().build().createParser(exchange); + MyHandler handler = new MyHandler(); + parser.parse(handler); + }; + } + + private static class MyHandler implements HttpHandler{ + final String PUBLIC_KEY = "BAPGG2IY3Vn48d_H8QNuVLRErkBI0L7oDOOCAMUBqYMTMTzukaIAuB5OOcmkdeRICcyQocEwD-oxVc81YXXZPRY"; + final String PRIVATE_KEY = "A7xDGuxMZ4ufflcAhBW23xpoWZNOLwM4Rw2wXjP0y6M"; + final String SUBJECT = "Foobarbaz"; + final String PAYLOAD = "My fancy message"; + + @Override + public void handleRequest(HttpServerExchange exchange) throws Exception { + FormData formData = exchange.getAttachment(FormDataParser.FORM_DATA); + String subscriptionJson = formData.get("subscriptionJson").getFirst().getValue(); + System.out.println(subscriptionJson); + + Security.addProvider(new BouncyCastleProvider()); + PushService pushService = new PushService(PUBLIC_KEY, PRIVATE_KEY, SUBJECT); + Subscription subscription = new Gson().fromJson(subscriptionJson, Subscription.class); + Notification notification = new Notification(subscription, PAYLOAD); + HttpResponse httpResponse = pushService.send(notification); + int statusCode = httpResponse.getStatusLine().getStatusCode(); + System.out.println(statusCode); + } + } +} diff --git a/src/test/resources/static/index.html b/src/test/resources/static/index.html new file mode 100644 index 0000000..d713279 --- /dev/null +++ b/src/test/resources/static/index.html @@ -0,0 +1,20 @@ + + + + + + +

Step 1

+ + Subscription: + +

Step 2

+ + + +

Step 3

+ +
    +
+ + diff --git a/src/test/resources/static/push.js b/src/test/resources/static/push.js new file mode 100644 index 0000000..8fa263f --- /dev/null +++ b/src/test/resources/static/push.js @@ -0,0 +1,145 @@ +window.addEventListener('load', registerServiceWorker, false); + +function registerServiceWorker() { + if ('serviceWorker' in navigator) { + navigator.serviceWorker.register('/sw.js').then(initialiseState); + } else { + console.warn('Service workers are not supported in this browser.'); + } +} + +function initialiseState() { + console.log('Service worker is registered.'); + + if (!('showNotification' in ServiceWorkerRegistration.prototype)) { + console.warn('Notifications aren\'t supported.'); + return; + } + + if (Notification.permission === 'denied') { + console.warn('The user has blocked notifications.'); + return; + } + + if (!('PushManager' in window)) { + console.warn('Push messaging isn\'t supported.'); + return; + } + + //var readyPromise = navigator.serviceWorker.ready; + var readyPromise = navigator.serviceWorker.getRegistration('./'); + readyPromise.then(function (serviceWorkerRegistration) { + console.log('Service worker is ready.'); + console.log(serviceWorkerRegistration.active && serviceWorkerRegistration.active.state) + + serviceWorkerRegistration.pushManager.getSubscription().then(function (subscription) { + console.log('Got subscription'); + console.log(subscription); + + if (!subscription) { + subscribe(); + + return; + } + + // Keep your server in sync with the latest subscriptionId + sendSubscriptionToServer(subscription); + }) + .catch(function (err) { + console.warn('Error during getSubscription()', err); + }); + }); +} + +function subscribe() { + const publicKey = base64UrlToUint8Array('BAPGG2IY3Vn48d_H8QNuVLRErkBI0L7oDOOCAMUBqYMTMTzukaIAuB5OOcmkdeRICcyQocEwD-oxVc81YXXZPRY'); + + //var readyPromise = navigator.serviceWorker.ready; + var readyPromise = navigator.serviceWorker.getRegistration('./'); + + readyPromise.then(function (serviceWorkerRegistration) { + serviceWorkerRegistration.pushManager.subscribe({ + userVisibleOnly: true, + applicationServerKey: publicKey + }) + .then(function (subscription) { + return sendSubscriptionToServer(subscription); + }) + .catch(function (e) { + if (Notification.permission === 'denied') { + console.warn('Permission for Notifications was denied'); + } else { + console.error('Unable to subscribe to push.', e); + } + }); + }); +} + +function sendSubscriptionToServer(subscription) { + var key = subscription.getKey ? subscription.getKey('p256dh') : ''; + var auth = subscription.getKey ? subscription.getKey('auth') : ''; + + document.getElementById('subscription').value = JSON.stringify(subscription); + + console.log({ + endpoint: subscription.endpoint, + key: key ? btoa(String.fromCharCode.apply(null, new Uint8Array(key))) : '', + auth: auth ? btoa(String.fromCharCode.apply(null, new Uint8Array(auth))) : '' + }); + + return Promise.resolve(); + + // Normally, you would actually send the subscription to the server: + return fetch('/profile/subscription', { + credentials: 'include', + headers: { + 'Content-Type': 'application/json' + }, + method: 'POST', + body: JSON.stringify({ + endpoint: subscription.endpoint, + key: key ? btoa(String.fromCharCode.apply(null, new Uint8Array(key))) : '', + auth: auth ? btoa(String.fromCharCode.apply(null, new Uint8Array(auth))) : '' + }) + }); +} + +function base64UrlToUint8Array(base64UrlData) { + const padding = '='.repeat((4 - base64UrlData.length % 4) % 4); + const base64 = (base64UrlData + padding) + .replace(/\-/g, '+') + .replace(/_/g, '/'); + + const rawData = atob(base64); + const buffer = new Uint8Array(rawData.length); + + for (let i = 0; i < rawData.length; ++i) { + buffer[i] = rawData.charCodeAt(i); + } + + return buffer; +} + +window.addEventListener('load', function () { + var button = document.getElementById('send'); + button.addEventListener('click', function () { + var subscription = document.getElementById('subscription').value; + + let formData = new FormData(); + formData.append('subscriptionJson', subscription); + + fetch('/send', { + method: 'POST', + body: formData + }); + }); +}); + +// Use Broadcast API to listen for messages from the service worker +var broadcast = new BroadcastChannel('message-received'); + +broadcast.onmessage = function (event) { + var li = document.createElement('li'); + li.innerText = event.data; + document.getElementById('messages').append(li); +}; diff --git a/src/test/resources/static/sw.js b/src/test/resources/static/sw.js new file mode 100644 index 0000000..503ba89 --- /dev/null +++ b/src/test/resources/static/sw.js @@ -0,0 +1,51 @@ +/** + * Service worker file. + * + * NOTE: This file MUST be located in the root. + */ + +'use strict'; + +const broadcast = new BroadcastChannel('message-received'); + +console.log('Started', self); + +self.addEventListener('install', function (event) { + self.skipWaiting(); + console.log('Installed', event); +}); + +self.addEventListener('activate', function (event) { + console.log('Activated', event); +}); + +self.addEventListener('push', function (event) { + console.log('Push message', event); + + var data = event.data.text(); + console.log('Push data: ' + data); + + // Broadcast message to the web page + broadcast.postMessage(event.data.text()); + + if (isJson(data)) { + var title = data.title; + var message = data.message; + } else { + var title = 'Push Message'; + var message = data; + } + + return self.registration.showNotification(title, { + body: 'Hello' + }); +}); + +var isJson = function (str) { + try { + JSON.parse(str); + } catch (e) { + return false; + } + return true; +} From 93fa8c735bc3a8079ff95310143fa63e77891139 Mon Sep 17 00:00:00 2001 From: Martijn Dwars Date: Tue, 7 Jul 2020 14:37:29 +0200 Subject: [PATCH 2/8] Add Chrome profile --- testcafe/chrome/.gitignore | 3 +++ testcafe/chrome/Default/Preferences | 13 +++++++++++++ testcafe/test.js | 11 +++++++++++ 3 files changed, 27 insertions(+) create mode 100644 testcafe/chrome/.gitignore create mode 100644 testcafe/chrome/Default/Preferences create mode 100644 testcafe/test.js diff --git a/testcafe/chrome/.gitignore b/testcafe/chrome/.gitignore new file mode 100644 index 0000000..9b579ca --- /dev/null +++ b/testcafe/chrome/.gitignore @@ -0,0 +1,3 @@ +* +!Default +!.gitignore diff --git a/testcafe/chrome/Default/Preferences b/testcafe/chrome/Default/Preferences new file mode 100644 index 0000000..ac33640 --- /dev/null +++ b/testcafe/chrome/Default/Preferences @@ -0,0 +1,13 @@ +{ + "profile": { + "content_settings": { + "exceptions": { + "notifications": { + "http://localhost:12345,*": { + "setting": 1 + } + } + } + } + } +} diff --git a/testcafe/test.js b/testcafe/test.js new file mode 100644 index 0000000..5e4b425 --- /dev/null +++ b/testcafe/test.js @@ -0,0 +1,11 @@ +import { Selector } from 'testcafe'; + +fixture `End-to-end test` + .page `http://localhost:5000`; + +test('End-to-end test', async t => { + await t + .expect(Selector('input#subscription').value).notEql('', 'subscription input is empty', { timeout: 100000 }) + .click('#send') + .expect(Selector('li').exists).ok('', { timeout: 100000 }); +}); From 8ab5591b6a0ba2674e73e121c82eca46d31fa3ae Mon Sep 17 00:00:00 2001 From: Martijn Dwars Date: Tue, 7 Jul 2020 14:12:39 +0200 Subject: [PATCH 3/8] Add Firefox profile --- testcafe/firefox/.gitignore | 1 + testcafe/firefox/AlternateServices.txt | 0 testcafe/firefox/OfflineCache/index.sqlite | Bin 0 -> 262144 bytes testcafe/firefox/SecurityPreloadState.txt | 0 testcafe/firefox/SiteSecurityServiceState.txt | 15 +++ testcafe/firefox/TRRBlacklist.txt | 0 testcafe/firefox/cert9.db | 0 testcafe/firefox/cert9.db-journal | Bin 0 -> 512 bytes ...0043-bb2b-a74e26bda56f.new-profile.jsonlz4 | Bin 0 -> 2819 bytes ...5-20ff-f04f-ad69-313d9f038d3c.main.jsonlz4 | Bin 0 -> 12825 bytes .../firefox/datareporting/session-state.json | 1 + testcafe/firefox/prefs.js | 110 +++++++++++++++++ testcafe/firefox/serviceworker.txt | 111 ++++++++++++++++++ testcafe/firefox/sessionCheckpoints.json | 1 + testcafe/firefox/sessionstore.jsonlz4 | Bin 0 -> 6136 bytes .../http+++localhost+12345/.metadata-v2 | Bin 0 -> 61 bytes .../http+++localhost+12345/cache/.padding | Bin 0 -> 8 bytes .../cache/caches.sqlite | Bin 0 -> 65536 bytes ...6b6cbf7-e374-624d-ba52-4c59c4f77bd3}.final | Bin 0 -> 1326 bytes testcafe/firefox/xulstore.json | 1 + 20 files changed, 240 insertions(+) create mode 100644 testcafe/firefox/.gitignore create mode 100644 testcafe/firefox/AlternateServices.txt create mode 100644 testcafe/firefox/OfflineCache/index.sqlite create mode 100644 testcafe/firefox/SecurityPreloadState.txt create mode 100644 testcafe/firefox/SiteSecurityServiceState.txt create mode 100644 testcafe/firefox/TRRBlacklist.txt create mode 100644 testcafe/firefox/cert9.db create mode 100644 testcafe/firefox/cert9.db-journal create mode 100644 testcafe/firefox/datareporting/archived/2020-07/1594124856200.b9addc63-b929-0043-bb2b-a74e26bda56f.new-profile.jsonlz4 create mode 100644 testcafe/firefox/datareporting/archived/2020-07/1594124856214.01b03e55-20ff-f04f-ad69-313d9f038d3c.main.jsonlz4 create mode 100644 testcafe/firefox/datareporting/session-state.json create mode 100644 testcafe/firefox/prefs.js create mode 100644 testcafe/firefox/serviceworker.txt create mode 100644 testcafe/firefox/sessionCheckpoints.json create mode 100644 testcafe/firefox/sessionstore.jsonlz4 create mode 100644 testcafe/firefox/storage/default/http+++localhost+12345/.metadata-v2 create mode 100644 testcafe/firefox/storage/default/http+++localhost+12345/cache/.padding create mode 100644 testcafe/firefox/storage/default/http+++localhost+12345/cache/caches.sqlite create mode 100644 testcafe/firefox/storage/default/http+++localhost+12345/cache/morgue/211/{e6b6cbf7-e374-624d-ba52-4c59c4f77bd3}.final create mode 100644 testcafe/firefox/xulstore.json diff --git a/testcafe/firefox/.gitignore b/testcafe/firefox/.gitignore new file mode 100644 index 0000000..72e8ffc --- /dev/null +++ b/testcafe/firefox/.gitignore @@ -0,0 +1 @@ +* diff --git a/testcafe/firefox/AlternateServices.txt b/testcafe/firefox/AlternateServices.txt new file mode 100644 index 0000000..e69de29 diff --git a/testcafe/firefox/OfflineCache/index.sqlite b/testcafe/firefox/OfflineCache/index.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..7d7daddf8f2672c4fa55b4512feb727ad9f9f724 GIT binary patch literal 262144 zcmeI*O>Y}T7yw|qUt3wq*@8<};y^4C1VR z#INDNk-xxi;Ljj-{1GQSr}2y4aVN>+uS)$=xm@}t zj!UIdRQ&E2zex+@JCi~2w|wb()@4-s{NXw@K;zuXlf+ciw&W%O7w5EruEa z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfWSNo3_gyc>i&cB;One=kiPEp-n80D z`(@hdCY|);C~2oB)63S(d+x9A)>oVLxUsWQe-{6rr^HWoZoDsE+9`J43p>{awR=%@ zuSo8@nH#s-hgsUqvxC{WMdRG|cN$OkKbYdxXU)!VeYj_1V>zDY`s>}OT3;*=KE0Oe ztMqJEj`u#C*!wH5WE3wwPS2L(NiMa)M|Yy?g92+mPkuN|Z#sjU=I)M@Xs)hp)n}YY zW^0Ux0>-6wTHLwRrglPrQr#l{B&4+dA^mL=Dp##bv?<}m)B>)*XM^{ zr2lQR990(=%fEkrwcLvZw{9*lR5qoKM{O(~7pFa*xjMg{=E+ch99`RbvQ{v}n`t*a zPV%hR9RP5B`5hXoE{8gmsW+)cJCm2k&R-P zLiN{2*(i-bA5Yjr8W-=$IFIqF>4{Wo3*moqKXe)a0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBnAUx9^CdVhZFS2+O!1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oMMYmC*G=rx74PfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z0D<`xSO}%}=eK^96Cgl<009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7csfgn%` zT|aah0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&Um|uZPD7`#+YCLq;-kQikR+YkT%D!c-! literal 0 HcmV?d00001 diff --git a/testcafe/firefox/datareporting/archived/2020-07/1594124856200.b9addc63-b929-0043-bb2b-a74e26bda56f.new-profile.jsonlz4 b/testcafe/firefox/datareporting/archived/2020-07/1594124856200.b9addc63-b929-0043-bb2b-a74e26bda56f.new-profile.jsonlz4 new file mode 100644 index 0000000000000000000000000000000000000000..770a12a57b50a4673ccc8a4848c63dd4a5b54a7d GIT binary patch literal 2819 zcmWkwYiu0V6+U+!`^sZ&$4MYSGaew7U}rP?@Y=fq;v5OI!NGhN7w}v^`mk zR`ta+t9p@|rcuag z^3SYV^=w_ELwS}h)J-~AXrQg(F&bFhsnB2wHa?Qov%01a;d3aHEoFyFxk5Ut z>$juj3iEt4R7?4kN?q5sj3TUqT3S=7XSA$<8A0fcy}mj(TX>=G6TFM`4}l zg`A!#4(125xZ=?Ejj%dld91-#7vZ0xIy9Wt(-~YcE#I|i_iz^?yWoGDSD0gR596uw z6P9gLw7}({!M%1G;Oix9H;s zjmv;7&cc1vYldw!evVu?g+D~ows4vu>$~f9y*O+R4r*q0Xh<`Q`9ZD7=%7aRY@H1a zv)pjLh_Ntd#qzi_4e$vd%LL*OeUy+H`1ciS%ox^ldSgg4vO3lBg?vLR)^(<3ax_Pa ztT=?9IHkTpu0HS;l6;#e@}Q7BPZG($k!bQaWMCs2x+V>nS6-o(P3y>vQs9N`4KnKm z4)YXp8let6s~$c`V1SgJ(6-NjyaVJ(LAVOUn;@A2fHQ({9mori=ib=ug0Kt3TL9+B zC0{KaJB}gyT|Z#$vj}>dwYk@=j;f_h0ewTpSO^=nt8{fc2&14IZkWP<9{!!waM|GA zy+TjcB;poved~VagqFjmoB$J-QX8~w*2$46~&xnRjAjooUHVE zW;i{R*3o5~8+3!zFsIts*nFA=x>P-5SWH!3lcS4X*O0HFk8u1#+_${esc(xDn~r5vhA=gk8woF2}Y^0gyn%W zpI@>Bc|A7CsA(gQ)l!4nKEpaSsT;7*OOG5|Q%&X@p5<-`(#Jt43Qtc}aHGCDtPd9g zc^+LeSU>24#cV&+{IHIB?FZqP@T0kMRV^Wgrf03ph{6sKw!*f}HTFUAAaB3UWCYjZ21x2x^_=X^S zNKR)@9mkY2dVhozg&LN$%l#QRjSg~`InCW-uGJa40>~DTJTHeqh!Lu^B*B+R)(vOh zljGMxEp$SknRVgOj&~4QXEiK#hagX&sMT!*%0{v?votYYKD015zPNDU;6izNYGP`< zvUp^CbZ-1WWqfY2S{|E%8mprMm~$k$A1HSN&2kjz_nv`X{!>Z51F~2*1%Zr5;6Jj$ zs;30_hJ->`rN$`dOS2x&EAXTp-*}Bc20NE5EC37TWM)#3$^VQG}tR#Uu zrJoI_RHQv?v%u@_1@TTHc2-mt<{ymdPGJ8YW<9nXvYn)N`kNw|lCU8698aAj$!kE^ z>$*DusUs$K!N}O8db}h4u!-_SZQRsAJtMDE!(eVOO`T>)n-@X)G{CVaIVlMTBJe!) z8^4z2bPTQs;RGQ@J1dx+%XOPa_UXX@k1d zumUXAjGiAJ94e&tz`K&)Vz!;e;xv{jtU)o-(GidvM7{xf*u&Aj^ajjg4#y@>#{~_f zYv3;(%WSeBZ8-<%$|$XX-mrX0?n(au$oFI7E`YB_4pPUg^M|D}Yc$vsqIg;oqk=xo zR*prK+$mSmP3}A{Jc9kKZrSM4*b5+EDL@aI!Jg><9Hc(;E$E$p1vaE{r-@>N9yB>` z+Dxl!=ED+^N$gq}!B$9bBj6KdmJUeIs>gWHV%~9VrxWnwBUaF=umC01FU`Qfh|fSx zxA!F`(%fr)F44Q+gKwi2aL>X6)KUjLoMAb&)8t)oVQw0aYz2Yqm-g=MbUNu{FqcNU z?_Gp#`?7*}bAb3}_xv%g-rRUE0T&^=XZhv_L^w}I#(0abk!984E14nfDKYxpNaE|8 zW;4V2LHz|%_xxbNXH}VO2k8tUZ;HM28M3|H1237prFq=8c^Hg*03R)_JsHnr(z$9> z_YHo0bAM(>H1a{TU)?Dx75W`PI14$;pZ`@%`YWj%VfFF7F=+?%v`3{r_n<7@D8Lt@ zLLTJLNH8snlT!S95ou%7`X&YO(YT`oEgC3(=(^kse4H`mO?X7Z!xi38Dm)@e!?CCN zq%wytX)|GP0V+tSN>P@xQO$Mx;m_FHQ68Kr2m7vVKL}-1>`^5KFT!E&pzL*{eO3x0 zA0fS`_M>A|oWXmKeUikJC&+!mlTmS+7!!CuP|B}DvPx>gf1`_jiwFKRiO%GTY5is4 zlmCK4bcre0uU%h3S}7OdpwjSo`xinMgT4lIy25&4J=#{VyJ0NU!*--_PjQ*orYD7h z-A!MR#D(O5GCve2dr0zeQqw!E-i`03cS!Nwna9YdezM>-(P0JEEYOsmcR^`btISt0 zLIq8gOCbIu61|JS^8|KEyQw8DUY33Y?PI-tYq37@e56;~0dY-Ca=Ev++!1O_=^z6) zK}|tr^yt1spRy7^rx@JEZ~8_EQIUv$iY8ZuD`R&@q7k__f0LZNM^113Rs?Q{5+z!j zwL|3QnS{)?-e!jFcGui0ezq?*vE@2-^p;HpC3FTiCyx?Iwj;tjk#mTVu@rFE9*c_a zM7*}uW{0}&nZ#^>U3Krvu_svC^3&WwKz2vr4+-Jzo~RiiuO(n&%k}%K7ol(M6&TT) zZFdVq3?jS+@K9pQMg+>6FLW=ru%^dD$6RPZ8D%}QxVgqwF*IwrkJMv{^D(%Tn4pf) MdNDSuUcP1hAD59XU;qFB literal 0 HcmV?d00001 diff --git a/testcafe/firefox/datareporting/archived/2020-07/1594124856214.01b03e55-20ff-f04f-ad69-313d9f038d3c.main.jsonlz4 b/testcafe/firefox/datareporting/archived/2020-07/1594124856214.01b03e55-20ff-f04f-ad69-313d9f038d3c.main.jsonlz4 new file mode 100644 index 0000000000000000000000000000000000000000..1e55717a6501c0d1af0f9381adb97c8d76f7d4fa GIT binary patch literal 12825 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 cN==%H&!Br;3-9Url{>4#^SkAth2= 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 TZ!qJ3*TX`Z#MaICZr%G|69 literal 0 HcmV?d00001 diff --git a/testcafe/firefox/storage/default/http+++localhost+12345/cache/caches.sqlite b/testcafe/firefox/storage/default/http+++localhost+12345/cache/caches.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..f4044f1da154e9f9c202bd11bf667c845f530173 GIT binary patch literal 65536 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 Ys4^{I)6$5C10w1ii#0AwiT_di4=OZf%>V!Z literal 0 HcmV?d00001 diff --git a/testcafe/firefox/xulstore.json b/testcafe/firefox/xulstore.json new file mode 100644 index 0000000..91cf717 --- /dev/null +++ b/testcafe/firefox/xulstore.json @@ -0,0 +1 @@ +{"chrome://browser/content/browser.xhtml":{"main-window":{"screenX":"4","screenY":"23","width":"1280","height":"1062","sizemode":"normal"},"sidebar-box":{"sidebarcommand":"","width":""},"sidebar-title":{"value":""}}} \ No newline at end of file From 22aedd07477efb0eac17803c143f79881512ae08 Mon Sep 17 00:00:00 2001 From: Martijn Dwars Date: Tue, 7 Jul 2020 15:28:22 +0200 Subject: [PATCH 4/8] Upgrade Gradle to 6.5.1 --- CHANGELOG.md | 5 ++ README.md | 26 +++++- build.gradle | 105 ----------------------- build.gradle.kts | 88 +++++++++++++++++++ gradle/wrapper/gradle-wrapper.jar | Bin 55741 -> 58702 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 51 ++++++----- gradlew.bat | 18 +++- release.gradle | 54 ------------ release.gradle.kts | 54 ++++++++++++ 10 files changed, 220 insertions(+), 183 deletions(-) delete mode 100644 build.gradle create mode 100644 build.gradle.kts delete mode 100644 release.gradle create mode 100644 release.gradle.kts diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b511c4..1995e62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 5.2.0 + +* Maintenance: Upgrade Gradle to 6.5.1 +* Maintenance: Convert build script to use Kotlin DSL instead of Groovy DSL + # 5.1.0 * Improvement: Add support for [urgency](https://tools.ietf.org/html/rfc8030#section-5.3) & [topic](https://tools.ietf.org/html/rfc8030#section-5.4) (contributed by jamie@checkin.tech). diff --git a/README.md b/README.md index c990d47..47e714f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # WebPush -A Web Push library for Java 7. Supports payloads and VAPID. +A Web Push library for Java 8. Supports payloads and VAPID. [![Build Status](https://travis-ci.org/web-push-libs/webpush-java.svg?branch=master)](https://travis-ci.org/web-push-libs/webpush-java) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/nl.martijndwars/web-push/badge.svg)](https://search.maven.org/search?q=g:nl.martijndwars%20AND%20a:web-push) @@ -123,7 +123,29 @@ for detailed usage instructions. If you plan on using VAPID, read [wiki/VAPID](h ## Testing -The integration tests use [Web Push Testing Service (WPTS)](https://github.com/GoogleChromeLabs/web-push-testing-service) to handle the Selenium and browser orchestrating. We use a forked version that fixes a bug on macOS. To install WPTS: +The e2e tests use testcafe. To install testcafe: + +``` +npm install -g testcafe +``` + +Then start the test server: + +``` +./gradlew test --tests '*TestCafeTest*' +``` + +Run the testcafe tests: + +``` +#/usr/bin/env bash +testcafe 'chrome --user-data-dir $(pwd)/testcafe/chrome' testcafe/test.js --hostname localhost --ports 12345,54321 +testcafe 'firefox -profile $(pwd)/testcafe/firefox' testcafe/test.js --hostname localhost --ports 12345,54321 +``` + +The integration tests use [Web Push Testing Service (WPTS)](https://github.com/GoogleChromeLabs/web-push-testing-service) to handle the Selenium and browser orchestrating. +We use a forked version that fixes a bug on macOS. +To install WPTS: ``` npm i -g github:MartijnDwars/web-push-testing-service#bump-selenium-assistant diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 9493098..0000000 --- a/build.gradle +++ /dev/null @@ -1,105 +0,0 @@ -plugins { - id 'application' - id 'com.github.johnrengelman.shadow' version '5.2.0' - - // Used by release.gradle - id 'maven-publish' - id 'signing' - id 'io.codearte.nexus-staging' version '0.21.1' -} - -apply plugin: 'application' -apply plugin: 'com.github.johnrengelman.shadow' - -group 'nl.martijndwars' -version '5.1.1-SNAPSHOT' - -repositories { - mavenCentral() -} - -dependencies { - // For CLI - compile group: 'com.beust', name: 'jcommander', version: '1.78' - - // For making async HTTP requests - compile group: 'org.apache.httpcomponents', name: 'httpasyncclient', version: '4.1.4' - - // For cryptographic operations - shadow group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.64' - - // For creating and signing JWT - compile group: 'org.bitbucket.b_c', name: 'jose4j', version: '0.7.0' - - // For parsing JSON - testCompile group: 'com.google.code.gson', name: 'gson', version: '2.8.6' - - // For making HTTP requests - testCompile group: 'org.apache.httpcomponents', name: 'fluent-hc', version: '4.5.10' - - // For testing, obviously - testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.2' - - // For running JUnit tests - testRuntime group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.2' - - // For turning InputStream to String - testCompile group: 'commons-io', name: 'commons-io', version: '2.6' - - // For reading the demo vapid keypair from a pem file - testCompile group: 'org.bouncycastle', name: 'bcpkix-jdk15on', version: '1.64' - - // For verifying Base64Encoder results in unit tests - testCompile group: 'com.google.guava', name: 'guava', version: '27.0.1-jre' - - // To run a local webserver in the TestCafe tests - testCompile group: 'io.undertow', name: 'undertow-servlet', version: '2.1.3.Final' -} - -wrapper { - gradleVersion = '5.1' -} - -compileJava { - sourceCompatibility = 1.7 - targetCompatibility = 1.7 -} - -compileTestJava { - sourceCompatibility = 1.8 -} - -mainClassName = 'nl.martijndwars.webpush.cli.Cli' - -run { - classpath configurations.shadow.files -} - -test { - useJUnitPlatform() - - testLogging { - events 'PASSED', 'FAILED', 'SKIPPED' - showStandardStreams true - exceptionFormat 'full' - } -} - -task javadocJar(type: Jar) { - classifier = 'javadoc' - from javadoc -} - -task sourcesJar(type: Jar) { - classifier = 'sources' - from sourceSets.main.allSource -} - -artifacts { - archives javadocJar - archives sourcesJar -} - -if (hasProperty('release')) { - apply from: 'release.gradle' -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..59ee418 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,88 @@ +import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL + +plugins { + application + id("com.github.johnrengelman.shadow") version "6.0.0" + + // Used by release.gradle.kts + `maven-publish` + signing + id("io.codearte.nexus-staging") version "0.21.1" +} + +group = "nl.martijndwars" +version = "5.1.1-SNAPSHOT" + +repositories { + mavenCentral() +} + +dependencies { + // For CLI + implementation(group = "com.beust", name = "jcommander", version = "1.78") + + // For making async HTTP requests + implementation(group = "org.apache.httpcomponents", name = "httpasyncclient", version = "4.1.4") + + // For cryptographic operations + shadow(group = "org.bouncycastle", name = "bcprov-jdk15on", version = "1.64") + + // For creating and signing JWT + implementation(group = "org.bitbucket.b_c", name = "jose4j", version = "0.7.0") + + // For parsing JSON + testImplementation(group = "com.google.code.gson", name = "gson", version = "2.8.6") + + // For making HTTP requests + testImplementation(group = "org.apache.httpcomponents", name = "fluent-hc", version = "4.5.10") + + // For testing, obviously + testImplementation(group = "org.junit.jupiter", name = "junit-jupiter-api", version = "5.5.2") + + // For running JUnit tests + testRuntimeOnly(group = "org.junit.jupiter", name = "junit-jupiter-engine", version = "5.5.2") + + // For turning InputStream to String + testImplementation(group = "commons-io", name = "commons-io", version = "2.6") + + // For reading the demo vapid keypair from a pem file + testImplementation(group = "org.bouncycastle", name = "bcpkix-jdk15on", version = "1.64") + + // For verifying Base64Encoder results in unit tests + testImplementation(group = "com.google.guava", name = "guava", version = "27.0.1-jre") + + // To run a local webserver in the TestCafe tests + testImplementation(group = "io.undertow", name = "undertow-servlet", version = "2.1.3.Final") +} + +tasks.named("wrapper") { + gradleVersion = "6.5.1" +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +application { + mainClassName = "nl.martijndwars.webpush.cli.Cli" +} + +tasks.named("run") { + classpath += files(configurations.shadow) +} + +tasks.named("test") { + useJUnitPlatform() + testLogging { + events("PASSED", "FAILED", "SKIPPED") + showStandardStreams = true + exceptionFormat = FULL + } +} + +if (hasProperty("release")) { + apply { + from("release.gradle.kts") + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 457aad0d98108420a977756b7145c93c8910b076..cc4fdc293d0e50b0ad9b65c16e7ddd1db2f6025b 100644 GIT binary patch delta 25889 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

Vp_F!2 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 delta 23210 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 T8$G=9f_Op3$oWUH6=nB-yb}(9 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9492014..bb8b2fc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index af6708f..2fe81a7 100755 --- a/gradlew +++ b/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m"' +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -109,8 +125,8 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` @@ -138,19 +154,19 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -159,14 +175,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 0f8d593..24467a1 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome diff --git a/release.gradle b/release.gradle deleted file mode 100644 index 4c09f1d..0000000 --- a/release.gradle +++ /dev/null @@ -1,54 +0,0 @@ -publishing { - publications { - mavenJava(MavenPublication) { - from components.java - artifact sourcesJar - artifact javadocJar - pom { - name = 'web-push' - description = 'A Web Push library for Java.' - url = 'https://github.com/web-push-libs/webpush-java' - scm { - connection = 'scm:git:git@github.com:web-push-libs/webpush-java.git' - developerConnection = 'scm:git:git@github.com:web-push-libs/webpush-java.git' - url = 'git@github.com:web-push-libs/webpush-java.git' - } - licenses { - license { - name = 'MIT License' - url = 'https://opensource.org/licenses/MIT' - } - } - developers { - developer { - id = 'martijndwars' - name = 'Martijn Dwars' - email = 'ikben@martijndwars.nl' - } - } - } - - } - } - repositories { - maven { - credentials { - username ossrhUsername - password ossrhPassword - } - url getRepositoryUrl() - } - } -} - -signing { - sign publishing.publications.mavenJava -} - -def getRepositoryUrl() { - if (version.endsWith('SNAPSHOT')) { - return 'https://oss.sonatype.org/content/repositories/snapshots/' - } else { - return 'https://oss.sonatype.org/service/local/staging/deploy/maven2/' - } -} diff --git a/release.gradle.kts b/release.gradle.kts new file mode 100644 index 0000000..aee1065 --- /dev/null +++ b/release.gradle.kts @@ -0,0 +1,54 @@ +val ossrhUsername: String by project +val ossrhPassword: String by project + +publishing { + publications { + create("mavenJava") { + from(components["java"]) + pom { + name.set("web-push") + description.set("A Web Push library for Java.") + url.set("https://github.com/web-push-libs/webpush-java") + scm { + connection.set("scm:git:git@github.com:web-push-libs/webpush-java.git") + developerConnection.set("scm:git:git@github.com:web-push-libs/webpush-java.git") + url.set("git@github.com:web-push-libs/webpush-java.git") + } + licenses { + license { + name.set("MIT License") + url.set("https://opensource.org/licenses/MIT") + } + } + developers { + developer { + id.set("martijndwars") + name.set("Martijn Dwars") + email.set("ikben@martijndwars.nl") + } + } + } + } + } + repositories { + maven { + credentials { + username = ossrhUsername + password = ossrhPassword + } + url = uri(getRepository()) + } + } +} + +signing { + sign(publishing.publications["mavenJava"]) +} + +fun getRepository() { + if (version.toString().endsWith("SNAPSHOT")) { + "https://oss.sonatype.org/content/repositories/snapshots/" + } else { + "https://oss.sonatype.org/service/local/staging/deploy/maven2/" + } +} From 9fee887bb1b6653310db44b2cd13f329851a30a3 Mon Sep 17 00:00:00 2001 From: Martijn Dwars Date: Tue, 7 Jul 2020 15:58:31 +0200 Subject: [PATCH 5/8] Remove Selenium tests --- .../webpush/selenium/BrowserTest.java | 78 --------- .../webpush/selenium/Configuration.java | 24 --- .../webpush/selenium/SeleniumTests.java | 81 --------- .../webpush/selenium/TestingService.java | 160 ------------------ 4 files changed, 343 deletions(-) delete mode 100644 src/test/java/nl/martijndwars/webpush/selenium/BrowserTest.java delete mode 100644 src/test/java/nl/martijndwars/webpush/selenium/Configuration.java delete mode 100644 src/test/java/nl/martijndwars/webpush/selenium/SeleniumTests.java delete mode 100644 src/test/java/nl/martijndwars/webpush/selenium/TestingService.java diff --git a/src/test/java/nl/martijndwars/webpush/selenium/BrowserTest.java b/src/test/java/nl/martijndwars/webpush/selenium/BrowserTest.java deleted file mode 100644 index 37ed6d2..0000000 --- a/src/test/java/nl/martijndwars/webpush/selenium/BrowserTest.java +++ /dev/null @@ -1,78 +0,0 @@ -package nl.martijndwars.webpush.selenium; - -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; -import nl.martijndwars.webpush.Notification; -import nl.martijndwars.webpush.PushService; -import nl.martijndwars.webpush.Subscription; -import org.apache.http.HttpResponse; -import org.junit.jupiter.api.function.Executable; - -import java.security.GeneralSecurityException; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class BrowserTest implements Executable { - public static final String GCM_API_KEY = "AIzaSyBAU0VfXoskxUSg81K5VgLgwblHbZWe6tA"; - public static final String PUBLIC_KEY = "BNFDO1MUnNpx0SuQyQcAAWYETa2+W8z/uc5sxByf/UZLHwAhFLwEDxS5iB654KHiryq0AxDhFXS7DVqXDKjjN+8="; - public static final String PRIVATE_KEY = "AM0aAyoIryzARADnIsSCwg1p1aWFAL3Idc8dNXpf74MH"; - public static final String VAPID_SUBJECT = "http://localhost:8090"; - - private TestingService testingService; - private Configuration configuration; - private int testSuiteId; - - public BrowserTest(TestingService testingService, Configuration configuration, int testSuiteId) { - this.configuration = configuration; - this.testingService = testingService; - this.testSuiteId = testSuiteId; - } - - /** - * Execute the test for the given browser configuration. - * - * @throws Throwable - */ - @Override - public void execute() throws Throwable { - PushService pushService = getPushService(); - - JsonObject test = testingService.getSubscription(testSuiteId, configuration); - - int testId = test.get("testId").getAsInt(); - - Subscription subscription = new Gson().fromJson(test.get("subscription").getAsJsonObject(), Subscription.class); - - String message = "Hëllö, world!"; - Notification notification = new Notification(subscription, message); - - HttpResponse response = pushService.send(notification); - assertEquals(201, response.getStatusLine().getStatusCode()); - - JsonArray messages = testingService.getNotificationStatus(testSuiteId, testId); - assertEquals(1, messages.size()); - assertEquals(new JsonPrimitive(message), messages.get(0)); - } - - protected PushService getPushService() throws GeneralSecurityException { - PushService pushService; - - if (!configuration.isVapid()) { - pushService = new PushService(GCM_API_KEY); - } else { - pushService = new PushService(PUBLIC_KEY, PRIVATE_KEY, VAPID_SUBJECT); - } - return pushService; - } - - /** - * The name used by JUnit to display the test. - * - * @return - */ - public String getDisplayName() { - return "Browser " + configuration.browser + ", version " + configuration.version + ", vapid " + configuration.isVapid(); - } -} diff --git a/src/test/java/nl/martijndwars/webpush/selenium/Configuration.java b/src/test/java/nl/martijndwars/webpush/selenium/Configuration.java deleted file mode 100644 index 5b4e4e1..0000000 --- a/src/test/java/nl/martijndwars/webpush/selenium/Configuration.java +++ /dev/null @@ -1,24 +0,0 @@ -package nl.martijndwars.webpush.selenium; - -public class Configuration { - protected final String browser; - protected final String version; - protected final String publicKey; - protected final String gcmSenderId; - - Configuration(String browser, String version, String publicKey, String gcmSenderId) { - this.browser = browser; - this.version = version; - this.publicKey = publicKey; - this.gcmSenderId = gcmSenderId; - } - - public boolean isVapid() { - return publicKey != null && !publicKey.isEmpty(); - } - - @Override - public String toString() { - return browser + ", " + version + ", " + publicKey; - } -} diff --git a/src/test/java/nl/martijndwars/webpush/selenium/SeleniumTests.java b/src/test/java/nl/martijndwars/webpush/selenium/SeleniumTests.java deleted file mode 100644 index de1353e..0000000 --- a/src/test/java/nl/martijndwars/webpush/selenium/SeleniumTests.java +++ /dev/null @@ -1,81 +0,0 @@ -package nl.martijndwars.webpush.selenium; - -import nl.martijndwars.webpush.Base64Encoder; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.DynamicTest; -import org.junit.jupiter.api.TestFactory; - -import java.io.IOException; -import java.security.Security; -import java.util.stream.Stream; - -import static org.junit.jupiter.api.DynamicTest.dynamicTest; - -/** - * SeleniumTest performs integration testing. - */ -public class SeleniumTests { - protected static final String GCM_SENDER_ID = "759071690750"; - protected static final String PUBLIC_KEY = "BNFDO1MUnNpx0SuQyQcAAWYETa2+W8z/uc5sxByf/UZLHwAhFLwEDxS5iB654KHiryq0AxDhFXS7DVqXDKjjN+8="; - - protected static TestingService testingService = new TestingService("http://localhost:8090/api/"); - protected static int testSuiteId; - - public SeleniumTests() { - Security.addProvider(new BouncyCastleProvider()); - } - - /** - * End the test suite. - * - * @throws IOException - */ - @AfterAll - public static void tearDown() throws IOException { - testingService.endTestSuite(testSuiteId); - } - - /** - * Generate a stream of tests based on the configurations. - * - * @return - */ - @TestFactory - public Stream dynamicTests() throws IOException { - testSuiteId = testingService.startTestSuite(); - - return getConfigurations().map(configuration -> { - BrowserTest browserTest = new BrowserTest(testingService, configuration, testSuiteId); - - return dynamicTest(browserTest.getDisplayName(), browserTest); - }); - } - - /** - * Get browser configurations to test. - * - * @return - */ - protected Stream getConfigurations() { - String PUBLIC_KEY_NO_PADDING = Base64Encoder.encodeWithoutPadding( - Base64Encoder.decode(PUBLIC_KEY) - ); - - return Stream.of( - new Configuration("chrome", "stable", null, GCM_SENDER_ID), - new Configuration("chrome", "beta", null, GCM_SENDER_ID), - //new Configuration("chrome", "unstable", null, GCM_SENDER_ID), See #90 - - new Configuration("firefox", "stable", null, GCM_SENDER_ID), - new Configuration("firefox", "beta", null, GCM_SENDER_ID), - - new Configuration("chrome", "stable", PUBLIC_KEY_NO_PADDING, null), - new Configuration("chrome", "beta", PUBLIC_KEY_NO_PADDING, null), - //new Configuration("chrome", "unstable", PUBLIC_KEY_NO_PADDING, null), See #90 - - new Configuration("firefox", "stable", PUBLIC_KEY_NO_PADDING, null), - new Configuration("firefox", "beta", PUBLIC_KEY_NO_PADDING, null) - ); - } -} diff --git a/src/test/java/nl/martijndwars/webpush/selenium/TestingService.java b/src/test/java/nl/martijndwars/webpush/selenium/TestingService.java deleted file mode 100644 index a89f9a9..0000000 --- a/src/test/java/nl/martijndwars/webpush/selenium/TestingService.java +++ /dev/null @@ -1,160 +0,0 @@ -package nl.martijndwars.webpush.selenium; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import org.apache.commons.io.IOUtils; -import org.apache.http.HttpEntity; -import org.apache.http.client.fluent.Request; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.util.EntityUtils; - -import java.io.IOException; - -import static java.nio.charset.StandardCharsets.UTF_8; - -/** - * Java wrapper for interacting with the Web Push Testing Service. - */ -public class TestingService { - private String baseUrl; - - public TestingService(String baseUrl) { - this.baseUrl = baseUrl; - } - - /** - * Start a new test suite. - * - * @return - */ - public int startTestSuite() throws IOException { - String startTestSuite = request(baseUrl + "start-test-suite/"); - - JsonElement root = JsonParser.parseString(startTestSuite); - - return root - .getAsJsonObject() - .get("data") - .getAsJsonObject() - .get("testSuiteId") - .getAsInt(); - } - - /** - * Get a test ID and subscription for the given test case. - * - * @param testSuiteId - * @param configuration - * @return - * @throws IOException - */ - public JsonObject getSubscription(int testSuiteId, Configuration configuration) throws IOException { - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("testSuiteId", testSuiteId); - jsonObject.addProperty("browserName", configuration.browser); - jsonObject.addProperty("browserVersion", configuration.version); - - if (configuration.gcmSenderId != null) { - jsonObject.addProperty("gcmSenderId", configuration.gcmSenderId); - } - - if (configuration.publicKey != null) { - jsonObject.addProperty("vapidPublicKey", configuration.publicKey); - } - - HttpEntity entity = new StringEntity(jsonObject.toString(), ContentType.APPLICATION_JSON); - - String getSubscription = request(baseUrl + "get-subscription/", entity); - - return getData(getSubscription); - } - - /** - * Get the notification status for the given test case. - * - * @param testSuiteId - * @param testId - * @return - * @throws IOException - */ - public JsonArray getNotificationStatus(int testSuiteId, int testId) throws IOException { - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("testSuiteId", testSuiteId); - jsonObject.addProperty("testId", testId); - - HttpEntity entity = new StringEntity(jsonObject.toString(), ContentType.APPLICATION_JSON); - - String notificationStatus = request(baseUrl + "get-notification-status/", entity); - - return getData(notificationStatus).get("messages").getAsJsonArray(); - } - - /** - * End the given test suite. - * - * @return - */ - public boolean endTestSuite(int testSuiteId) throws IOException { - JsonObject jsonObject = new JsonObject(); - jsonObject.addProperty("testSuiteId", testSuiteId); - - HttpEntity entity = new StringEntity(jsonObject.toString(), ContentType.APPLICATION_JSON); - - String endTestSuite = request(baseUrl + "end-test-suite/", entity); - - return getData(endTestSuite).get("success").getAsBoolean(); - } - - /** - * Perform HTTP request and return response. - * - * @param uri - * @return - */ - protected String request(String uri) throws IOException { - return request(uri, null); - } - - /** - * Perform HTTP request and return response. - * - * @param uri - * @return - */ - protected String request(String uri, HttpEntity entity) throws IOException { - return Request.Post(uri).body(entity).execute().handleResponse(httpResponse -> { - String json = EntityUtils.toString(httpResponse.getEntity()); - - if (httpResponse.getStatusLine().getStatusCode() != 200) { - JsonElement root = JsonParser.parseString(json); - JsonObject error = root.getAsJsonObject().get("error").getAsJsonObject(); - - String errorId = error.get("id").getAsString(); - String errorMessage = error.get("message").getAsString(); - - String body = IOUtils.toString(entity.getContent(), UTF_8); - - throw new IllegalStateException("Error while requesting " + uri + " with body " + body + " (" + errorId + ": " + errorMessage); - } - - return json; - }); - } - - /** - * Get the a JSON object of the data in the JSON response. - * - * @param response - */ - protected JsonObject getData(String response) { - JsonElement root = JsonParser.parseString(response); - - return root - .getAsJsonObject() - .get("data") - .getAsJsonObject(); - } -} From 8d94ea0947a1f7679b7146f53ec83cdad1bbf303 Mon Sep 17 00:00:00 2001 From: Martijn Dwars Date: Tue, 7 Jul 2020 15:59:19 +0200 Subject: [PATCH 6/8] Run Testcafe as part of Travis CI --- .travis.yml | 23 +++++++++----- README.md | 30 ++----------------- build.gradle.kts | 6 ++++ .../webpush/{testcafe => }/Webserver.java | 5 +--- .../webpush/testcafe/TestCafeTest.java | 19 ------------ src/test/resources/static/push.js | 2 +- src/test/resources/static/sw.js | 6 ++-- testcafe/test.js | 9 ++++-- 8 files changed, 37 insertions(+), 63 deletions(-) rename src/test/java/nl/martijndwars/webpush/{testcafe => }/Webserver.java (94%) delete mode 100644 src/test/java/nl/martijndwars/webpush/testcafe/TestCafeTest.java diff --git a/.travis.yml b/.travis.yml index b0df96c..c35bbdc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,11 @@ +dist: bionic language: java sudo: required +addons: + chrome: stable + firefox: latest +services: + - xvfb jdk: - openjdk8 - openjdk11 @@ -12,20 +18,23 @@ before_cache: - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ before_install: - - nvm i node -install: - - npm install github:GoogleChromeLabs/web-push-testing-service -g -before_script: - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start || echo \"Unable to start virtual display.\"" - sleep 3 + - nvm i node +install: + - npm i -g testcafe +before_script: + - fluxbox >/dev/null 2>&1 & + - sleep 5 script: - - web-push-testing-service start wpts - ./gradlew clean check - - web-push-testing-service stop wpts + - ./gradlew :startE2eTestServer & + - sleep 5 + - testcafe 'chrome --user-data-dir $(pwd)/testcafe/chrome' testcafe/test.js --hostname localhost --ports 12345,54321 --skip-js-errors + - testcafe 'firefox -profile $(pwd)/testcafe/firefox' testcafe/test.js --hostname localhost --ports 12345,54321 cache: directories: - $HOME/.gradle/caches/ - $HOME/.gradle/wrapper/ - - ~/.selenium-assistant - node_modules diff --git a/README.md b/README.md index 47e714f..d76ba8a 100644 --- a/README.md +++ b/README.md @@ -132,43 +132,17 @@ npm install -g testcafe Then start the test server: ``` -./gradlew test --tests '*TestCafeTest*' +./gradlew :startE2eTestServer ``` Run the testcafe tests: ``` #/usr/bin/env bash -testcafe 'chrome --user-data-dir $(pwd)/testcafe/chrome' testcafe/test.js --hostname localhost --ports 12345,54321 +testcafe 'chrome --user-data-dir $(pwd)/testcafe/chrome' testcafe/test.js --hostname localhost --ports 12345,54321 --skip-js-errors testcafe 'firefox -profile $(pwd)/testcafe/firefox' testcafe/test.js --hostname localhost --ports 12345,54321 ``` -The integration tests use [Web Push Testing Service (WPTS)](https://github.com/GoogleChromeLabs/web-push-testing-service) to handle the Selenium and browser orchestrating. -We use a forked version that fixes a bug on macOS. -To install WPTS: - -``` -npm i -g github:MartijnDwars/web-push-testing-service#bump-selenium-assistant -``` - -Then start WPTS: - -``` -web-push-testing-service start wpts -``` - -Then run the tests: - -``` -./gradlew clean test -``` - -Finally, stop WPTS: - -``` -web-push-testing-service stop wpts -``` - ## FAQ ### Why does encryption take multiple seconds? diff --git a/build.gradle.kts b/build.gradle.kts index 59ee418..a52486d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -81,6 +81,12 @@ tasks.named("test") { } } +val startE2eTestServer = task("startE2eTestServer") { + description = "Start e2e test server." + classpath = sourceSets["test"].runtimeClasspath + main = "nl.martijndwars.webpush.Webserver" +} + if (hasProperty("release")) { apply { from("release.gradle.kts") diff --git a/src/test/java/nl/martijndwars/webpush/testcafe/Webserver.java b/src/test/java/nl/martijndwars/webpush/Webserver.java similarity index 94% rename from src/test/java/nl/martijndwars/webpush/testcafe/Webserver.java rename to src/test/java/nl/martijndwars/webpush/Webserver.java index e6381b2..d2af4bd 100644 --- a/src/test/java/nl/martijndwars/webpush/testcafe/Webserver.java +++ b/src/test/java/nl/martijndwars/webpush/Webserver.java @@ -1,4 +1,4 @@ -package nl.martijndwars.webpush.testcafe; +package nl.martijndwars.webpush; import java.nio.file.Paths; import java.security.Security; @@ -13,9 +13,6 @@ import io.undertow.server.handlers.form.FormDataParser; import io.undertow.server.handlers.form.FormParserFactory; import io.undertow.server.handlers.resource.PathResourceManager; -import nl.martijndwars.webpush.Notification; -import nl.martijndwars.webpush.PushService; -import nl.martijndwars.webpush.Subscription; import org.apache.http.HttpResponse; import org.bouncycastle.jce.provider.BouncyCastleProvider; diff --git a/src/test/java/nl/martijndwars/webpush/testcafe/TestCafeTest.java b/src/test/java/nl/martijndwars/webpush/testcafe/TestCafeTest.java deleted file mode 100644 index d7045b2..0000000 --- a/src/test/java/nl/martijndwars/webpush/testcafe/TestCafeTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package nl.martijndwars.webpush.testcafe; - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -public class TestCafeTest { - public static Webserver webserver; - - @BeforeAll - public static void startWebserver() { - webserver = new Webserver(); - webserver.start(); - } - - @Test - public void testEndToEnd() throws InterruptedException { - Thread.sleep(60 * 1000); - } -} diff --git a/src/test/resources/static/push.js b/src/test/resources/static/push.js index 8fa263f..cf1cc5a 100644 --- a/src/test/resources/static/push.js +++ b/src/test/resources/static/push.js @@ -140,6 +140,6 @@ var broadcast = new BroadcastChannel('message-received'); broadcast.onmessage = function (event) { var li = document.createElement('li'); - li.innerText = event.data; + li.innerText = event.data.text; document.getElementById('messages').append(li); }; diff --git a/src/test/resources/static/sw.js b/src/test/resources/static/sw.js index 503ba89..1d0d937 100644 --- a/src/test/resources/static/sw.js +++ b/src/test/resources/static/sw.js @@ -6,7 +6,7 @@ 'use strict'; -const broadcast = new BroadcastChannel('message-received'); +var broadcast = new BroadcastChannel('message-received'); console.log('Started', self); @@ -26,7 +26,9 @@ self.addEventListener('push', function (event) { console.log('Push data: ' + data); // Broadcast message to the web page - broadcast.postMessage(event.data.text()); + broadcast.postMessage({ + text: event.data.text() + }); if (isJson(data)) { var title = data.title; diff --git a/testcafe/test.js b/testcafe/test.js index 5e4b425..a832791 100644 --- a/testcafe/test.js +++ b/testcafe/test.js @@ -3,9 +3,14 @@ import { Selector } from 'testcafe'; fixture `End-to-end test` .page `http://localhost:5000`; +const subscription = Selector('input#subscription'); +const options = { + timeout: 30000 +}; + test('End-to-end test', async t => { await t - .expect(Selector('input#subscription').value).notEql('', 'subscription input is empty', { timeout: 100000 }) + .expect(subscription.value).notEql('', 'subscription input is empty', options) .click('#send') - .expect(Selector('li').exists).ok('', { timeout: 100000 }); + .expect(Selector('li').exists).ok('', options); }); From 8d3e25af4091a27accbabed4cdbe6a3fb2404de1 Mon Sep 17 00:00:00 2001 From: Martijn Dwars Date: Tue, 7 Jul 2020 16:53:08 +0200 Subject: [PATCH 7/8] Change xvfb command --- .travis.yml | 8 ++------ src/test/resources/static/index.html | 22 ++++++++++++++++++++++ src/test/resources/static/push.js | 15 +++++++++++++-- testcafe/test.js | 6 ++++++ 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index c35bbdc..f734424 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ dist: bionic language: java -sudo: required addons: chrome: stable firefox: latest @@ -18,9 +17,6 @@ before_cache: - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ before_install: - - "export DISPLAY=:99.0" - - "sh -e /etc/init.d/xvfb start || echo \"Unable to start virtual display.\"" - - sleep 3 - nvm i node install: - npm i -g testcafe @@ -31,8 +27,8 @@ script: - ./gradlew clean check - ./gradlew :startE2eTestServer & - sleep 5 - - testcafe 'chrome --user-data-dir $(pwd)/testcafe/chrome' testcafe/test.js --hostname localhost --ports 12345,54321 --skip-js-errors - - testcafe 'firefox -profile $(pwd)/testcafe/firefox' testcafe/test.js --hostname localhost --ports 12345,54321 + - xvfb-run -a testcafe 'chrome --user-data-dir $(pwd)/testcafe/chrome' testcafe/test.js --hostname localhost --ports 12345,54321 --skip-js-errors + - xvfb-run -a testcafe 'firefox -profile $(pwd)/testcafe/firefox' testcafe/test.js --hostname localhost --ports 12345,54321 cache: directories: - $HOME/.gradle/caches/ diff --git a/src/test/resources/static/index.html b/src/test/resources/static/index.html index d713279..ace15b2 100644 --- a/src/test/resources/static/index.html +++ b/src/test/resources/static/index.html @@ -6,6 +6,28 @@

Step 1

+
+
Service worker supported?
+
+ +
Show notification supported?
+
+ +
Notification permission?
+
+ +
Push Manager?
+
+ +
Service worker ready?
+
+ +
Subscription ready?
+
+
+ +

Step 2

+ Subscription:

Step 2

diff --git a/src/test/resources/static/push.js b/src/test/resources/static/push.js index cf1cc5a..985db27 100644 --- a/src/test/resources/static/push.js +++ b/src/test/resources/static/push.js @@ -2,8 +2,10 @@ window.addEventListener('load', registerServiceWorker, false); function registerServiceWorker() { if ('serviceWorker' in navigator) { + document.getElementById('service-worker').append('y'); navigator.serviceWorker.register('/sw.js').then(initialiseState); } else { + document.getElementById('service-worker').append('n'); console.warn('Service workers are not supported in this browser.'); } } @@ -13,28 +15,37 @@ function initialiseState() { if (!('showNotification' in ServiceWorkerRegistration.prototype)) { console.warn('Notifications aren\'t supported.'); + document.getElementById('show-notification').append('n'); return; + } else { + document.getElementById('show-notification').append('y'); } if (Notification.permission === 'denied') { console.warn('The user has blocked notifications.'); + document.getElementById('notification-permission').append('n'); return; + } else { + document.getElementById('notification-permission').append('y'); } if (!('PushManager' in window)) { console.warn('Push messaging isn\'t supported.'); + document.getElementById('push-manager').append('n'); return; + } else { + document.getElementById('push-manager').append('y'); } //var readyPromise = navigator.serviceWorker.ready; var readyPromise = navigator.serviceWorker.getRegistration('./'); readyPromise.then(function (serviceWorkerRegistration) { console.log('Service worker is ready.'); - console.log(serviceWorkerRegistration.active && serviceWorkerRegistration.active.state) + document.getElementById('service-worker-ready').append('y'); serviceWorkerRegistration.pushManager.getSubscription().then(function (subscription) { console.log('Got subscription'); - console.log(subscription); + document.getElementById('subscription-ready').append('y'); if (!subscription) { subscribe(); diff --git a/testcafe/test.js b/testcafe/test.js index a832791..524b0b4 100644 --- a/testcafe/test.js +++ b/testcafe/test.js @@ -10,6 +10,12 @@ const options = { test('End-to-end test', async t => { await t + .expect(Selector('dd#service-worker').innerText).eql('y', '', options) + .expect(Selector('dd#show-notification').innerText).eql('y', '', options) + .expect(Selector('dd#notification-permission').innerText).eql('y', '', options) + .expect(Selector('dd#push-manager').innerText).eql('y', '', options) + .expect(Selector('dd#service-worker-ready').innerText).eql('y', '', options) + .expect(Selector('dd#subscription-ready').innerText).eql('y', '', options) .expect(subscription.value).notEql('', 'subscription input is empty', options) .click('#send') .expect(Selector('li').exists).ok('', options); From a3be9bd2a6d9f6251f3ff0dac66355b8788689d7 Mon Sep 17 00:00:00 2001 From: Martijn Dwars Date: Wed, 8 Jul 2020 11:07:37 +0200 Subject: [PATCH 8/8] More debug --- src/test/resources/static/index.html | 6 ++++ src/test/resources/static/push.js | 52 ++++++++++++---------------- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/src/test/resources/static/index.html b/src/test/resources/static/index.html index ace15b2..48fdae0 100644 --- a/src/test/resources/static/index.html +++ b/src/test/resources/static/index.html @@ -24,6 +24,12 @@

Step 1

Subscription ready?
+ +
Service worker ready 2?
+
+ +
Subscription ready 2?
+

Step 2

diff --git a/src/test/resources/static/push.js b/src/test/resources/static/push.js index 985db27..08b9a7a 100644 --- a/src/test/resources/static/push.js +++ b/src/test/resources/static/push.js @@ -45,16 +45,16 @@ function initialiseState() { serviceWorkerRegistration.pushManager.getSubscription().then(function (subscription) { console.log('Got subscription'); - document.getElementById('subscription-ready').append('y'); - + console.log(subscription); + if (!subscription) { - subscribe(); - - return; + subscribe().then(function () { + document.getElementById('subscription-ready').append('y'); + }); + } else { + sendSubscriptionToServer(subscription); + document.getElementById('subscription-ready').append('y'); } - - // Keep your server in sync with the latest subscriptionId - sendSubscriptionToServer(subscription); }) .catch(function (err) { console.warn('Error during getSubscription()', err); @@ -64,23 +64,29 @@ function initialiseState() { function subscribe() { const publicKey = base64UrlToUint8Array('BAPGG2IY3Vn48d_H8QNuVLRErkBI0L7oDOOCAMUBqYMTMTzukaIAuB5OOcmkdeRICcyQocEwD-oxVc81YXXZPRY'); + const subscribeOptions = { + userVisibleOnly: true, + applicationServerKey: publicKey + }; //var readyPromise = navigator.serviceWorker.ready; var readyPromise = navigator.serviceWorker.getRegistration('./'); - readyPromise.then(function (serviceWorkerRegistration) { - serviceWorkerRegistration.pushManager.subscribe({ - userVisibleOnly: true, - applicationServerKey: publicKey - }) - .then(function (subscription) { + return readyPromise.then(function (serviceWorkerRegistration) { + document.getElementById('service-worker-ready-2').append('y'); + + return serviceWorkerRegistration.pushManager.subscribe(subscribeOptions).then(function (subscription) { + document.getElementById('subscription-ready').append('y'); + document.getElementById('subscription-ready-2').append('y'); + return sendSubscriptionToServer(subscription); - }) - .catch(function (e) { + }).catch(function (e) { if (Notification.permission === 'denied') { console.warn('Permission for Notifications was denied'); + document.getElementById('subscription-ready-2').append('Permission denied'); } else { console.error('Unable to subscribe to push.', e); + document.getElementById('subscription-ready-2').append('Unable to subscribe: ' + e); } }); }); @@ -99,20 +105,6 @@ function sendSubscriptionToServer(subscription) { }); return Promise.resolve(); - - // Normally, you would actually send the subscription to the server: - return fetch('/profile/subscription', { - credentials: 'include', - headers: { - 'Content-Type': 'application/json' - }, - method: 'POST', - body: JSON.stringify({ - endpoint: subscription.endpoint, - key: key ? btoa(String.fromCharCode.apply(null, new Uint8Array(key))) : '', - auth: auth ? btoa(String.fromCharCode.apply(null, new Uint8Array(auth))) : '' - }) - }); } function base64UrlToUint8Array(base64UrlData) {