Unverified Commit e00b9cf3 authored by Robert Knight's avatar Robert Knight Committed by GitHub

Merge pull request #1196 from hypothesis/use-main-frame-main-url-for-group-search

Always use main URL from main frame when searching for groups
parents 28cc2570 11dde525
...@@ -35,15 +35,11 @@ function groups( ...@@ -35,15 +35,11 @@ function groups(
function getDocumentUriForGroupSearch() { function getDocumentUriForGroupSearch() {
function mainUri() { function mainUri() {
const uris = store.searchUris(); const mainFrame = store.mainFrame();
if (uris.length === 0) { if (!mainFrame) {
return null; return null;
} }
return mainFrame.uri;
// We get the first HTTP URL here on the assumption that group scopes must
// be domains (+paths)? and therefore we need to look up groups based on
// HTTP URLs (so eg. we cannot use a "file:" URL or PDF fingerprint).
return uris.find(uri => uri.startsWith('http'));
} }
return awaitStateChange(store, mainUri); return awaitStateChange(store, mainUri);
} }
......
...@@ -57,7 +57,7 @@ describe('groups', function() { ...@@ -57,7 +57,7 @@ describe('groups', function() {
fakeStore = fakeReduxStore( fakeStore = fakeReduxStore(
{ {
searchUris: ['http://example.org'], mainFrame: { uri: 'http://example.org' },
focusedGroup: null, focusedGroup: null,
groups: [], groups: [],
directLinkedGroupId: null, directLinkedGroupId: null,
...@@ -76,8 +76,8 @@ describe('groups', function() { ...@@ -76,8 +76,8 @@ describe('groups', function() {
focusedGroup() { focusedGroup() {
return this.getState().focusedGroup; return this.getState().focusedGroup;
}, },
searchUris() { mainFrame() {
return this.getState().searchUris; return this.getState().mainFrame;
}, },
focusedGroupId() { focusedGroupId() {
const group = this.getState().focusedGroup; const group = this.getState().focusedGroup;
...@@ -388,9 +388,9 @@ describe('groups', function() { ...@@ -388,9 +388,9 @@ describe('groups', function() {
it('waits for the document URL to be determined', () => { it('waits for the document URL to be determined', () => {
const svc = service(); const svc = service();
fakeStore.setState({ searchUris: [] }); fakeStore.setState({ mainFrame: null });
const loaded = svc.load(); const loaded = svc.load();
fakeStore.setState({ searchUris: ['https://asite.com'] }); fakeStore.setState({ mainFrame: { uri: 'https://asite.com' } });
return loaded.then(() => { return loaded.then(() => {
assert.calledWith(fakeApi.groups.list, { assert.calledWith(fakeApi.groups.list, {
...@@ -407,7 +407,7 @@ describe('groups', function() { ...@@ -407,7 +407,7 @@ describe('groups', function() {
}); });
it('does not wait for the document URL', () => { it('does not wait for the document URL', () => {
fakeStore.setState({ searchUris: [] }); fakeStore.setState({ mainFrame: null });
const svc = service(); const svc = service();
return svc.load().then(() => { return svc.load().then(() => {
assert.calledWith(fakeApi.groups.list, { assert.calledWith(fakeApi.groups.list, {
...@@ -775,14 +775,16 @@ describe('groups', function() { ...@@ -775,14 +775,16 @@ describe('groups', function() {
it('should refetch groups if main frame URL has changed', () => { it('should refetch groups if main frame URL has changed', () => {
const svc = service(); const svc = service();
fakeStore.setState({ searchUris: ['https://domain.com/page-a'] }); fakeStore.setState({ mainFrame: { uri: 'https://domain.com/page-a' } });
return svc return svc
.load() .load()
.then(() => { .then(() => {
// Simulate main frame URL change, eg. due to client-side navigation in // Simulate main frame URL change, eg. due to client-side navigation in
// a single page application. // a single page application.
fakeApi.groups.list.resetHistory(); fakeApi.groups.list.resetHistory();
fakeStore.setState({ searchUris: ['https://domain.com/page-b'] }); fakeStore.setState({
mainFrame: { uri: 'https://domain.com/page-b' },
});
return fakeRootScope.eventCallbacks[events.FRAME_CONNECTED](); return fakeRootScope.eventCallbacks[events.FRAME_CONNECTED]();
}) })
...@@ -794,7 +796,7 @@ describe('groups', function() { ...@@ -794,7 +796,7 @@ describe('groups', function() {
it('should not refetch groups if main frame URL has not changed', () => { it('should not refetch groups if main frame URL has not changed', () => {
const svc = service(); const svc = service();
fakeStore.setState({ searchUris: ['https://domain.com/page-a'] }); fakeStore.setState({ mainFrame: { uri: 'https://domain.com/page-a' } });
return svc return svc
.load() .load()
.then(() => { .then(() => {
......
'use strict'; 'use strict';
const { createSelector } = require('reselect');
const util = require('../util'); const util = require('../util');
function init() { function init() {
...@@ -71,6 +73,24 @@ function frames(state) { ...@@ -71,6 +73,24 @@ function frames(state) {
return state.frames; return state.frames;
} }
/**
* Return the "main" frame that the sidebar is connected to.
*
* The sidebar may be connected to multiple frames from different URLs.
* For some purposes, the main frame (typically the top-level one that contains
* the sidebar) needs to be distinguished. This selector returns the main frame
* for that purpose.
*
* This may be `null` during startup.
*/
const mainFrame = createSelector(
state => state.frames,
// Sub-frames will all have a "frame identifier" set. The main frame is the
// one with a `null` id.
frames => frames.find(f => !f.id) || null
);
function searchUrisForFrame(frame) { function searchUrisForFrame(frame) {
let uris = [frame.uri]; let uris = [frame.uri];
...@@ -113,6 +133,7 @@ module.exports = { ...@@ -113,6 +133,7 @@ module.exports = {
selectors: { selectors: {
frames, frames,
mainFrame,
searchUris, searchUris,
}, },
}; };
'use strict'; 'use strict';
const frames = require('../frames'); const frames = require('../frames');
const createStore = require('../../create-store');
const session = require('../session'); const session = require('../session');
const util = require('../../util'); const util = require('../../util');
const unroll = require('../../../../shared/test/util').unroll; const unroll = require('../../../../shared/test/util').unroll;
...@@ -13,7 +15,16 @@ function init() { ...@@ -13,7 +15,16 @@ function init() {
return Object.assign({}, frames.init(), session.init()); return Object.assign({}, frames.init(), session.init());
} }
describe('frames reducer', function() { describe('sidebar/store/modules/frames', function() {
let store;
beforeEach(() => {
// Setup a store for tests. Note that some of the tests in this module
// pre-date the `createStore` helper and have not been refactored to use
// it yet.
store = createStore([frames]);
});
describe('#connectFrame', function() { describe('#connectFrame', function() {
it('adds the frame to the list of connected frames', function() { it('adds the frame to the list of connected frames', function() {
const frame = { uri: 'http://example.com' }; const frame = { uri: 'http://example.com' };
...@@ -71,6 +82,34 @@ describe('frames reducer', function() { ...@@ -71,6 +82,34 @@ describe('frames reducer', function() {
}); });
}); });
describe('#mainFrame', () => {
it('returns `null` if no frames are connected', () => {
assert.isNull(store.mainFrame());
});
[
{
frames: [{ id: null, uri: 'https://example.org' }],
expectedFrame: 0,
},
{
frames: [
// An iframe which is also connected.
{ id: 'iframe1', uri: 'https://foo.com/' },
// The top-level frame.
{ id: null, uri: 'https://example.org' },
],
expectedFrame: 1,
},
].forEach(({ frames, expectedFrame }) => {
it('returns the main frame from the frames connected to the sidebar', () => {
frames.forEach(frame => store.connectFrame(frame));
assert.equal(store.mainFrame(), frames[expectedFrame]);
});
});
});
describe('#searchUris', function() { describe('#searchUris', function() {
unroll( unroll(
'returns the expected search URIs (#when)', 'returns the expected search URIs (#when)',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment