Add activity push notifications and registration patches
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
const {test} = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const vm = require('node:vm');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const source = fs.readFileSync(path.join(__dirname, '../static/js/native-push.js'), 'utf8');
|
||||
const id = '12345678-1234-1234-1234-123456789abc';
|
||||
const tag = 'a'.repeat(64);
|
||||
|
||||
async function setup(options={}) {
|
||||
const listeners = {}, navigations = [], elements = [];
|
||||
const session = {authenticated:true, session_tag:tag, ...options.session};
|
||||
const config = {textContent:JSON.stringify({openNotification:'Open new notification'})};
|
||||
const main = {prepend(node) { elements.push(node); }};
|
||||
const document = {
|
||||
getElementById(name) { return name === 'native-push-config' ? config : null; },
|
||||
querySelector(name) { return name === 'main' ? main : null; },
|
||||
createElement(type) { return {type,children:[],append(node){this.children.push(node);},setAttribute(){}}; },
|
||||
addEventListener() {}, body:main,
|
||||
};
|
||||
const push = {
|
||||
addListener(name, callback) { listeners[name]=callback; return Promise.resolve(); },
|
||||
checkPermissions:async()=>({receive:'granted'}), register:async()=>{},
|
||||
};
|
||||
const device = {getInfo:async()=>({deviceId:id,appVersion:'1.1.0',binding:options.binding ?? tag}), prepareSession:async()=>{}};
|
||||
vm.runInNewContext(source, {document, window:{Capacitor:{getPlatform:()=> 'android',Plugins:{PushNotifications:push,MetalCircleDevice:device}},addEventListener(){}},
|
||||
location:{pathname:'/',assign(value){navigations.push(value);}},
|
||||
fetch:async()=>({ok:true,json:async()=>session}), localStorage:{getItem(){return 'seen';}}});
|
||||
await new Promise(resolve=>setImmediate(resolve));
|
||||
return {listeners,navigations,elements};
|
||||
}
|
||||
|
||||
test('tap opens only backend-resolved destination for the matching session',async()=>{
|
||||
const app=await setup();
|
||||
await app.listeners.pushNotificationActionPerformed({notification:{data:{notification_id:id,session_tag:tag,url:'https://evil.invalid'}}});
|
||||
assert.deepEqual(app.navigations,['/notifications/'+id]);
|
||||
});
|
||||
test('old-account push cannot navigate after user switch',async()=>{
|
||||
const app=await setup({session:{session_tag:'b'.repeat(64)}});
|
||||
await app.listeners.pushNotificationActionPerformed({notification:{data:{notification_id:id,session_tag:tag}}});
|
||||
assert.deepEqual(app.navigations,[]);
|
||||
});
|
||||
test('logout and native binding mismatch cannot open a notification',async()=>{
|
||||
for(const options of [{session:{authenticated:false}}, {binding:''}]) {
|
||||
const app=await setup(options);
|
||||
await app.listeners.pushNotificationActionPerformed({notification:{data:{notification_id:id,session_tag:tag}}});
|
||||
assert.deepEqual(app.navigations,[]);
|
||||
}
|
||||
});
|
||||
test('arbitrary URL and malformed identifier are ignored',async()=>{
|
||||
const app=await setup();
|
||||
for(const data of [{url:'https://evil.invalid'}, {notification_id:'../../profile',session_tag:tag}])
|
||||
await app.listeners.pushNotificationActionPerformed({notification:{data}});
|
||||
assert.deepEqual(app.navigations,[]);
|
||||
});
|
||||
test('foreground hint uses local text and never remote HTML',async()=>{
|
||||
const app=await setup();
|
||||
await app.listeners.pushNotificationReceived({body:'<script>private message</script>',data:{notification_id:id,session_tag:tag}});
|
||||
assert.equal(app.elements.length,1);
|
||||
const link=app.elements[0].children[0];
|
||||
assert.equal(link.textContent,'Open new notification');
|
||||
assert.equal(link.href,'/notifications/'+id);
|
||||
assert.equal(link.innerHTML,undefined);
|
||||
});
|
||||
Reference in New Issue
Block a user