Commit 2ea51788 authored by Robert Knight's avatar Robert Knight Committed by Hannah Stepanek

Support filtering annotations by display name (#937)

* Support filtering annotations by display name

Allow `user:{term}` filters in the search bar to match display names as
well as usernames.

I have allowed for annotations having no `user_info` field as with other
code in the client that handles annotation objects. However "h" will always
populate that field currently.

Fixes https://github.com/hypothesis/product-backlog/issues/943

* Add tests to clarify how quotes are handled in filter query parsing

This came out of a Slack discussion [1] about how the client handles
quoted and unquoted terms in filter queries.

[1] https://hypothes-is.slack.com/archives/C4K6M7P5E/p1549466094062700

* Test that all matching annotations are returned for "user" queries

Respond to CR feedback to extend the current set of tests for "user"
queries.
parent 96d55c90
......@@ -57,6 +57,21 @@ describe('sidebar.search-filter', () => {
assert.equal(result.any[2], 'hi-fi');
assert.equal(result.any[3], 'a:bc');
});
it('supports quoting terms', () => {
const parsed = searchFilter.toObject('user:"Dan Whaley"');
assert.deepEqual(parsed, {
user: ['Dan Whaley'],
});
});
it('assigns unquoted terms to "any" category', () => {
const parsed = searchFilter.toObject('user:Dan Whaley');
assert.deepEqual(parsed, {
any: ['Whaley'],
user: ['Dan'],
});
});
});
describe('#generateFacetedFilter', () => {
......
......@@ -132,6 +132,60 @@ describe('sidebar/services/view-filter', () => {
});
});
describe('"user" field', () => {
let id = 0;
function annotationWithUser(username, displayName = null) {
++id;
return {
id,
user: `acct:${username}@example.com`,
user_info: {
display_name: displayName,
},
};
}
function userQuery(term) {
return { user: { terms: [term], operator: 'or' } };
}
it('matches username', () => {
const anns = [
annotationWithUser('johnsmith'),
annotationWithUser('jamesdean'),
annotationWithUser('johnjones'),
];
const result = viewFilter.filter(anns, userQuery('john'));
assert.deepEqual(result, [anns[0].id, anns[2].id]);
});
it("matches user's display name if present", () => {
const anns = [
// Users with display names set.
annotationWithUser('jsmith', 'John Smith'),
annotationWithUser('jdean', 'James Dean'),
annotationWithUser('jherriot', 'James Herriot'),
annotationWithUser('jadejames', 'Jade'),
// User with no display name.
annotationWithUser('fmercury'),
// Annotation with no extended user info.
{ id: 100, user: 'acct:jim@example.com' },
];
const result = viewFilter.filter(anns, userQuery('james'));
assert.deepEqual(result, [anns[1].id, anns[2].id, anns[3].id]);
});
it('ignores display name if not set', () => {
const anns = [annotationWithUser('msmith')];
const result = viewFilter.filter(anns, userQuery('null'));
assert.deepEqual(result, []);
});
});
describe('"since" field', () => {
it('matches if the annotation is newer than the query', () => {
const annotation = {
......
......@@ -4,6 +4,13 @@
// breaks browserify-ngannotate.
let unused; // eslint-disable-line
function displayName(ann) {
if (!ann.user_info) {
return '';
}
return ann.user_info.display_name || '';
}
/**
* Filter annotations against parsed search queries.
*
......@@ -133,7 +140,7 @@ function viewFilter(unicode) {
},
user: {
autofalse: ann => typeof ann.user !== 'string',
value: ann => ann.user,
value: ann => ann.user + ' ' + displayName(ann),
match: (term, value) => value.indexOf(term) > -1,
},
};
......
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