Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Broadcast store changes to popup #1

Merged
merged 1 commit into from
Aug 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions src/wrap-store/wrapStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
DEFAULT_PORT_NAME
} from '../constants';
import { withSerializer, withDeserializer, noop } from "../serialization";
import {getBrowserAPI} from '../util';
import { getBrowserAPI } from '../util';
import shallowDiff from '../strategies/shallowDiff/diff';

/**
Expand Down Expand Up @@ -95,20 +95,22 @@ export default (store, {
* Setup for state updates
*/
const serializedMessagePoster = withSerializer(serializer)((...args) => {
const onErrorCallback = () => {
if (browserAPI.runtime.lastError) {
// do nothing - errors can be present
// if no content script exists on receiver
}
};

browserAPI.runtime.sendMessage(...args, onErrorCallback);
// We will broadcast state changes to all tabs to sync state across content scripts
return browserAPI.tabs.query({}, (tabs) => {
for (const tab of tabs) {
browserAPI.tabs.sendMessage(tab.id, ...args, () => {
if (chrome.runtime.lastError) {
// do nothing - errors can be present
// if no content script exists on receiver
}
});
browserAPI.tabs.sendMessage(tab.id, ...args, onErrorCallback);
}
});
});


let currentState = store.getState();

const patchState = () => {
Expand Down
77 changes: 40 additions & 37 deletions test/wrapStore.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,36 @@ import '@babel/polyfill';
import sinon from 'sinon';
import should from 'should';

import {wrapStore} from '../src';
import { wrapStore } from '../src';
import shallowDiff from '../src/strategies/shallowDiff/diff';
import {DISPATCH_TYPE, STATE_TYPE, PATCH_STATE_TYPE} from '../src/constants';
import { DISPATCH_TYPE, STATE_TYPE, PATCH_STATE_TYPE } from '../src/constants';

describe('wrapStore', function () {
describe('wrapStore', function() {
const portName = 'test';

beforeEach(function () {
beforeEach(function() {
global.self = {};
const tabs = [1];

// Mock chrome.runtime API
self.chrome = {
runtime: {
onMessage: {
addListener: () => {},
addListener: () => { },
},
onMessageExternal: {
addListener: () => {},
addListener: () => { },
},
onConnectExternal: {
addListener: () => {},
addListener: () => { },
},
sendMessage: () => { }
},
tabs: {
query: (tabObject, cb) => {
cb(tabs);
},
sendMessage: () => {}
sendMessage: () => { }
}
};
});
Expand All @@ -55,22 +56,23 @@ describe('wrapStore', function () {
onConnectExternal: {
addListener: fn => listeners.onConnectExternal.push(fn),
},
sendMessage: () => { }
},
tabs: {
query: (tabObject, cb) => {
cb(tabs);
},
sendMessage: () => {}
sendMessage: () => { }
}
};

return listeners;
}

describe("on receiving messages", function () {
describe("on receiving messages", function() {
let listeners, store, payload, message, sender, callback;

beforeEach(function () {
beforeEach(function() {
listeners = setupListeners();
store = {
dispatch: sinon.spy(),
Expand All @@ -89,11 +91,11 @@ describe('wrapStore', function () {
payload
};
sender = {};
callback = () => {}; // noop. Maybe should validate it is invoked?
callback = () => { }; // noop. Maybe should validate it is invoked?
});

it('should dispatch actions received on onMessage to store', function () {
wrapStore(store, {portName});
it('should dispatch actions received on onMessage to store', function() {
wrapStore(store, { portName });
listeners.onMessage.forEach(l => l(message, sender, callback));

store.dispatch.calledOnce.should.eql(true);
Expand All @@ -106,18 +108,18 @@ describe('wrapStore', function () {
.should.eql(true);
});

it('should not dispatch actions received on onMessage for other ports', function () {
wrapStore(store, {portName});
it('should not dispatch actions received on onMessage for other ports', function() {
wrapStore(store, { portName });
message.portName = portName + '2';
listeners.onMessage.forEach(l => l(message, sender, callback));

store.dispatch.notCalled.should.eql(true);
});

it('should deserialize incoming messages correctly', function () {
it('should deserialize incoming messages correctly', function() {
const deserializer = sinon.spy(JSON.parse);

wrapStore(store, {portName, deserializer});
wrapStore(store, { portName, deserializer });
message.payload = JSON.stringify(payload);
listeners.onMessage.forEach(l => l(message, sender, callback));

Expand All @@ -131,10 +133,10 @@ describe('wrapStore', function () {
.should.eql(true);
});

it('should not deserialize incoming messages for other ports', function () {
it('should not deserialize incoming messages for other ports', function() {
const deserializer = sinon.spy(JSON.parse);

wrapStore(store, {portName, deserializer});
wrapStore(store, { portName, deserializer });
message.portName = portName + '2';
message.payload = JSON.stringify(payload);
listeners.onMessage.forEach(l => l(message, sender, callback));
Expand All @@ -143,7 +145,7 @@ describe('wrapStore', function () {
});
});

it('should serialize initial state and subsequent patches correctly', function () {
it('should serialize initial state and subsequent patches correctly', function() {
const listeners = setupListeners();

const sendMessage = (self.chrome.tabs.sendMessage = sinon.spy());
Expand Down Expand Up @@ -175,7 +177,7 @@ describe('wrapStore', function () {

const serializer = (payload) => JSON.stringify(payload);

wrapStore(store, {portName, serializer});
wrapStore(store, { portName, serializer });

// Listen for state changes
listeners.onMessage.forEach(l => l(tabs));
Expand All @@ -198,7 +200,7 @@ describe('wrapStore', function () {
sendMessage.secondCall.args[1].should.eql(expectedPatchMessage);
});

it('should use the provided diff strategy', function () {
it('should use the provided diff strategy', function() {
const listeners = setupListeners();
const sendMessage = (self.chrome.tabs.sendMessage = sinon.spy());

Expand Down Expand Up @@ -228,7 +230,7 @@ describe('wrapStore', function () {
oldObj, newObj
}]);

wrapStore(store, {portName, diffStrategy});
wrapStore(store, { portName, diffStrategy });

// Listen for state changes
listeners.onMessage.forEach(l => l({ portName }));
Expand All @@ -246,7 +248,7 @@ describe('wrapStore', function () {
sendMessage.secondCall.args[1].should.eql(expectedPatchMessage);
});

describe("when validating options", function () {
describe("when validating options", function() {
const store = {
dispatch: sinon.spy(),
subscribe: () => {
Expand All @@ -255,33 +257,33 @@ describe('wrapStore', function () {
getState: () => ({})
};

it('should use defaults if no options present', function () {
it('should use defaults if no options present', function() {
should.doesNotThrow(() => wrapStore(store));
});

it('should throw an error if serializer is not a function', function () {
it('should throw an error if serializer is not a function', function() {
should.throws(() => {
wrapStore(store, { portName, serializer: "abc" });
}, Error);
});

it('should throw an error if deserializer is not a function', function () {
it('should throw an error if deserializer is not a function', function() {
should.throws(() => {
wrapStore(store, {portName, deserializer: "abc"});
wrapStore(store, { portName, deserializer: "abc" });
}, Error);
});

it('should throw an error if diffStrategy is not a function', function () {
it('should throw an error if diffStrategy is not a function', function() {
should.throws(() => {
wrapStore(store, {portName, diffStrategy: "abc"});
wrapStore(store, { portName, diffStrategy: "abc" });
}, Error);
});
});

it(
'should send a safety message to all tabs once initialized',
function () {
const tabs = [123,456,789,1011,1213];
function() {
const tabs = [123, 456, 789, 1011, 1213];
const tabResponders = [];
const store = {
dispatch: sinon.spy(),
Expand All @@ -294,14 +296,15 @@ describe('wrapStore', function () {
self.chrome = {
runtime: {
onMessage: {
addListener: () => {},
addListener: () => { },
},
onMessageExternal: {
addListener: () => {},
addListener: () => { },
},
onConnectExternal: {
addListener: () => {},
addListener: () => { },
},
sendMessage: () => { }
},
tabs: {
query: (tabObject, cb) => {
Expand All @@ -313,7 +316,7 @@ describe('wrapStore', function () {
}
};

wrapStore(store, {portName});
wrapStore(store, { portName });

tabResponders.length.should.equal(5);
},
Expand Down