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

Reconnect to the port if not ready upon safety message #281

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
20 changes: 14 additions & 6 deletions src/store/Store.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,22 +43,31 @@ class Store {
}

this.portName = portName;
this.deserializer = deserializer;
this.readyResolved = false;
this.readyPromise = new Promise(resolve => this.readyResolve = resolve);

this.browserAPI = getBrowserAPI();
this.extensionId = extensionId; // keep the extensionId as an instance variable
this.port = this.browserAPI.runtime.connect(this.extensionId, {name: portName});
this.safetyHandler = this.safetyHandler.bind(this);
if (this.browserAPI.runtime.onMessage) {
this.safetyMessage = this.browserAPI.runtime.onMessage.addListener(this.safetyHandler);
}
this.serializedPortListener = withDeserializer(deserializer)((...args) => this.port.onMessage.addListener(...args));
this.serializedMessageSender = withSerializer(serializer)((...args) => this.browserAPI.runtime.sendMessage(...args), 1);
this.listeners = [];
this.state = state;
this.patchStrategy = patchStrategy;
this.dispatch = this.dispatch.bind(this); // add this context to dispatch
this.setupPort();
}

/**
* Connects to the port and sets up the serialized port listener
*/
setupPort() {
this.port = this.browserAPI.runtime.connect(this.extensionId, {name: this.portName});

this.serializedPortListener = withDeserializer(this.deserializer)((...args) => this.port.onMessage.addListener(...args));
// Don't use shouldDeserialize here, since no one else should be using this port
this.serializedPortListener(message => {
switch (message.type) {
Expand All @@ -80,7 +89,6 @@ class Store {
}
});

this.dispatch = this.dispatch.bind(this); // add this context to dispatch
}

/**
Expand Down Expand Up @@ -184,10 +192,10 @@ class Store {
// Remove Saftey Listener
this.browserAPI.runtime.onMessage.removeListener(this.safetyHandler);

// Resolve if readyPromise has not been resolved.
// This indicates that we connected before the background was ready, re-setup the connection to receive initial state
if(!this.readyResolved) {
this.readyResolved = true;
this.readyResolve();
this.port.disconnect();
this.setupPort();
}
}
}
Expand Down
33 changes: 25 additions & 8 deletions test/Store.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,16 @@ describe("Store", function () {
// mock onMessage listeners array
const safetyListeners = [];

const disconnectSpy = sinon.spy();
// override mock chrome API for this test

self.chrome.runtime = {
connect: () => {
return {
onMessage: {
addListener: () => {},
},
disconnect: disconnectSpy
};
},
onMessage: {
Expand All @@ -177,26 +180,32 @@ describe("Store", function () {

// make readyResolve() a spy function
store.readyResolve = sinon.spy();
store.setupPort = sinon.spy();

// send message
l({ action: "storeReady", portName });

safetyListeners.length.should.equal(0);
store.readyResolved.should.eql(true);
store.readyResolve.calledOnce.should.equal(true);
store.readyResolved.should.eql(false);
store.readyResolve.callCount.should.equal(0);
store.setupPort.callCount.should.equal(1);
disconnectSpy.callCount.should.equal(1);
});

it("should setup a safety listener per portName", function () {
// mock onMessage listeners array
const safetyListeners = [];

const disconnectSpy = sinon.spy();
// override mock chrome API for this test

self.chrome.runtime = {
connect: () => {
return {
onMessage: {
addListener: () => {},
},
disconnect: disconnectSpy
};
},
onMessage: {
Expand Down Expand Up @@ -224,26 +233,34 @@ describe("Store", function () {

// make readyResolve() a spy function
store.readyResolve = sinon.spy();
store.setupPort = sinon.spy();
store2.readyResolve = sinon.spy();
store2.setupPort = sinon.spy();

// send message for port 1
l1({ action: "storeReady", portName });
l2({ action: "storeReady", portName });

safetyListeners.length.should.equal(1);
store.readyResolved.should.eql(true);
store.readyResolve.calledOnce.should.equal(true);
store.readyResolved.should.eql(false);
store.readyResolve.calledOnce.should.equal(false);
store.setupPort.calledOnce.should.equal(true);
store2.readyResolved.should.eql(false);
store2.readyResolve.calledOnce.should.equal(false);
store2.setupPort.calledOnce.should.equal(false);
disconnectSpy.callCount.should.equal(1);

// send message for port 2
l1({ action: "storeReady", portName: portName2 });
l2({ action: "storeReady", portName: portName2 });
safetyListeners.length.should.equal(0);
store.readyResolved.should.eql(true);
store.readyResolve.calledOnce.should.equal(true);
store2.readyResolved.should.eql(true);
store2.readyResolve.calledOnce.should.equal(true);
store.readyResolved.should.eql(false);
store.readyResolve.calledOnce.should.equal(false);
store.setupPort.calledOnce.should.equal(true);
store2.readyResolved.should.eql(false);
store2.readyResolve.calledOnce.should.equal(false);
store2.setupPort.calledOnce.should.equal(true);
disconnectSpy.callCount.should.equal(2);
});
});

Expand Down