-
Notifications
You must be signed in to change notification settings - Fork 54
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
add request cache, change Request to extend Body, fix Body, implemen… #85
add request cache, change Request to extend Body, fix Body, implemen… #85
Conversation
…t client.postMessage
@@ -2,14 +2,19 @@ const generateRandomId = require('../utils/generateRandomId'); | |||
|
|||
// https://developer.mozilla.org/en-US/docs/Web/API/Client | |||
class Client { | |||
constructor(url, frameType) { | |||
constructor(url, options) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like passing in a postMessageCallback
is not part of the Client spec, and causes a backwards incompatibility if someone is passing in frameType currently. Can you provide more details of the reasoning for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've passed this callback in order to pass a mocked function to properly test if post-message was called. I can pass is to a different parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool. Can you share a snippet of what your test looks like? We might be able to find a better way than adding non-spec code to the mocks 🤞🏻
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it('should update the clients', async done => {
const defer = when.defer();
const clientPostCallback = response => {
defer.resolve(response);
};
const responseBody = { test: 'response' };
responseMock = new Response(responseBody, { headers: headersMock });
global.fetch = jest.fn(() => responseMock);
await clients.openWindow(
'http://localhost:3000',
clientPostCallback
);
requestMock = new Request('http://localhost:3000/test', {
cache: 'reload'
});
expect(global.fetch.mock.calls.length).toBe(0);
const response = await self.trigger('fetch', requestMock);
expect(global.fetch.mock.calls.length).toBe(1);
expect(response).toEqual(responseMock);
setTimeout(() => {
defer.reject('Post message has not been called from the client');
}, 2000);
when(defer.promise).then(
callbackResponse => {
expect(callbackResponse.data).toEqual(responseBody);
done();
},
failure => {
done(failure);
}
);
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome. Can you point me to when in this example postMessage
should be called?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the test, it will be called when we intercept the fetch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const updateClients = async (event, response) => {
const clients = await self.clients.matchAll();
const data = await response.json();
const { clientId } = event;
clients.forEach(client => {
if (clientId !== client.id) {
client.postMessage({
data
});
}
});
};
const cachedResourceOrFetch = async event => {
// some more logic
const response = fetch('url');
updateClients(event, response.clone());
return response.clone();
};
self.addEventListener('fetch', async event => {
event.respondWith(cachedResourceOrFetch(event));
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. It's hard to suggest a better way to test it without the full example, but I think the simplest way would be to override the mock on the client itself. That way we're not adding non-spec code to the models themselves.
const client = await clients.openWindow('http://localhost:3000');
client.postMessage = jest.fn();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
^ Does that work for you? This should make it easy to verify that postMessage is being called (but that's not really a great unit to test).
I'm trying to have the mocks act exactly like the spec, and adding extra parameters is undesirable. There's no good way to update the postMessage method itself since that talks to a separate DOM environment. The only better way would be to use puppeteer and write a unit test to verify that the postMessage is working correctly in your app.
@@ -15,8 +15,8 @@ class Clients { | |||
return Promise.resolve(this.clients); | |||
} | |||
|
|||
openWindow(url) { | |||
const client = new Client(url); | |||
openWindow(url, postMessageCallback) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not look like part of the spec.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above.
} else if (typeof body !== 'object') { | ||
body = undefined; | ||
} | ||
super(body); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need the if statements? I think it should just be
constructor(body, init) {
super(body);
...
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When you clone the Response the body will be already a Blob
We disabled Travis on this repo, but somehow your diff got in a weird state with it. I'll be able to get it merged once we resolve comments! |
Sorry, I was out for some weeks, I've removed the implementation of the post message. |
this.body = body || ''; | ||
if (typeof body === 'object' && !(body instanceof Blob)) { | ||
body = JSON.stringify(body); | ||
} else if (typeof body !== 'object') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain this line? I believe it will break if body
is a string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JSON.stringify() does not break with a string
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typeof 'foo' === 'string'
, so it won't get into that if statement, instead it will be set to undefined
. That's why I was curious about the else if
statement. What was the reasoning behind the fallback to undefined
?
I've found my way to this PR as I'm writing up a test harness for my scripts on Cloudflare's Workers environment. Specifically I found that Is there anything I can do to help the code review above to move this forward? (Thank you for this project!) |
Resolved in #92 |
add request cache
change Request to extend Body
fix Body
implement client.postmessage to receive a callback function