Commit 734244b9 authored by Robert Knight's avatar Robert Knight

Convert sidebar/services/view-filter tests to JS

Convert the tests for the `viewFilter` class to JS. This is mostly a
straightforward conversion except that `viewFilter.fields` has been made
a private variable and the direct tests for ithave been removed, since the
contents of this field were an implementation detail of the class.
parent dd75b323
{module, inject} = angular.mock
# Return an ISO date string for a `Date` that is `age` seconds ago.
isoDateWithAge = (age) ->
new Date(Date.now() - age * 1000).toISOString()
poem =
tiger: 'Tiger! Tiger! burning bright
In the forest of the night
What immortal hand or eye
Could frame thy fearful symmetry?'
raven: 'Once upon a midnight dreary, while I pondered, weak and weary,
Over many a quaint and curious volume of forgotten lore—
While I nodded, nearly napping, suddenly there came a tapping,
As of some one gently rapping, rapping at my chamber door.
“’Tis some visitor,” I muttered, “tapping at my chamber door—
Only this and nothing more.”'
describe 'viewFilter', ->
sandbox = null
fakeUnicode = null
viewFilter = null
before ->
angular.module('h', [])
.service('viewFilter', require('../view-filter'))
beforeEach module('h')
beforeEach module ($provide) ->
sandbox = sinon.sandbox.create()
fakeUnicode = {
fold: sinon.stub().returnsArg(0)
normalize: sinon.stub().returnsArg(0)
}
$provide.value('unicode', fakeUnicode)
return
beforeEach inject (_viewFilter_) ->
viewFilter = _viewFilter_
afterEach ->
sandbox.restore()
describe 'filter', ->
it 'normalizes the filter terms', ->
filters =
text:
terms: ['Tiger']
operator: 'and'
viewFilter.filter [], filters
assert.calledWith fakeUnicode.fold, 'Tiger'
describe 'filter operators', ->
annotations = null
beforeEach ->
annotations = [
{id: 1, text: poem.tiger},
{id: 2, text: poem.raven}
]
it 'all terms must match for "and" operator', ->
filters =
text:
terms: ['Tiger', 'burning', 'bright']
operator: 'and'
result = viewFilter.filter annotations, filters
assert.equal result.length, 1
assert.equal result[0], 1
it 'only one term must match for "or" operator', ->
filters =
text:
terms: ['Tiger', 'quaint']
operator: 'or'
result = viewFilter.filter annotations, filters
assert.equal result.length, 2
describe 'fields', ->
describe 'autofalse', ->
it 'consider auto false function', ->
viewFilter.fields =
test:
autofalse: sandbox.stub().returns(true)
value: (annotation) -> return annotation.test
match: (term, value) -> return value.indexOf(term) > -1
filters =
test:
terms: ['Tiger']
operator: 'and'
annotations = [{id: 1, test: poem.tiger}]
result = viewFilter.filter annotations, filters
assert.called viewFilter.fields.test.autofalse
assert.equal result.length, 0
it 'uses the value function to extract data from the annotation', ->
viewFilter.fields =
test:
autofalse: (annotation) -> return false
value: sandbox.stub().returns('test')
match: (term, value) -> return value.indexOf(term) > -1
filters =
test:
terms: ['test']
operator: 'and'
annotations = [{id: 1, test: poem.tiger}]
result = viewFilter.filter annotations, filters
assert.called viewFilter.fields.test.value
assert.equal result.length, 1
it 'the match function determines the matching', ->
viewFilter.fields =
test:
autofalse: (annotation) -> return false
value: (annotation) -> return annotation.test
match: sandbox.stub().returns(false)
filters =
test:
terms: ['Tiger']
operator: 'and'
annotations = [{id: 1, test: poem.tiger}]
result = viewFilter.filter annotations, filters
assert.called viewFilter.fields.test.match
assert.equal result.length, 0
viewFilter.fields.test.match.returns(true)
result = viewFilter.filter annotations, filters
assert.called viewFilter.fields.test.match
assert.equal result.length, 1
describe 'any field', ->
it 'finds matches across many fields', ->
annotation1 = {id: 1, text: poem.tiger}
annotation2 = {id: 2, user: poem.tiger}
annotation3 = {id: 3, tags: ['Tiger']}
annotations = [annotation1, annotation2, annotation3]
filters =
any:
terms: ['Tiger']
operator: 'and'
result = viewFilter.filter annotations, filters
assert.equal result.length, 3
it 'can find terms across different fields', ->
annotation =
id:1
text: poem.tiger
target: [
selector: [{
"type": "TextQuoteSelector",
"exact": "The Tiger by William Blake",
}]
]
user: "acct:poe@edgar.com"
tags: ["poem", "Blake", "Tiger"]
filters =
any:
terms: ['burning', 'William', 'poem', 'bright']
operator: 'and'
result = viewFilter.filter [annotation], filters
assert.equal result.length, 1
assert.equal result[0], 1
describe '"uri" field', ->
it "matches if the query occurs in the annotation's URI", ->
ann =
id: 1
uri: 'https://publisher.org/article'
filters =
uri:
terms: ['publisher']
operator: 'or'
result = viewFilter.filter [ann], filters
assert.deepEqual result, [1]
describe '"since" field', ->
it 'matches if the annotation is newer than the query', ->
ann =
id: 1
updated: isoDateWithAge(50)
filters =
since:
terms: [100]
operator: 'and'
result = viewFilter.filter [ann], filters
assert.deepEqual result, [1]
it 'does not match if the annotation is older than the query', ->
ann =
id: 1
updated: isoDateWithAge(150)
filters =
since:
terms: [100]
operator: 'and'
result = viewFilter.filter [ann], filters
assert.deepEqual result, []
it 'ignores filters with no terms in the query', ->
ann = { id: 1, tags: ['foo'] }
filters =
any:
terms: ['foo']
operator: 'and'
tag:
terms: []
operator: 'and'
result = viewFilter.filter [ann], filters
assert.deepEqual result, [1]
'use strict';
const ViewFilter = require('../view-filter');
function isoDateWithAge(age) {
return new Date(Date.now() - age * 1000).toISOString();
}
const poem = {
tiger: `Tiger! Tiger! burning bright
In the forest of the night
When immortal hand or eye
Could frame thy fearful symmetry?`,
raven: `Once upon a midnight dreary, when I pondered, weak and weary,
Over many a quaint and curious volume of forgotten lore-
While I nodded, nearly napping, suddely there came a tapping,
As of some one gently rapping, rapping at my chamber door.
“’Tis some visitor,” I muttered, “tapping at my chamber door—
Only this and nothing more.”`,
};
describe('sidebar/services/view-filter', () => {
let viewFilter;
let fakeUnicode;
beforeEach(() => {
fakeUnicode = {
fold: sinon.stub().returnsArg(0),
normalize: sinon.stub().returnsArg(0),
};
viewFilter = new ViewFilter(fakeUnicode);
});
describe('#filter', () => {
it('applies unicode-aware case folding to filter terms', () => {
const filters = {
text: { terms: ['Tiger'], operator: 'and' },
};
viewFilter.filter([], filters);
assert.calledWith(fakeUnicode.fold, 'Tiger');
});
});
describe('filter operators', () => {
let annotations;
beforeEach(() => {
annotations = [
{ id: 1, text: poem.tiger },
{ id: 2, text: poem.raven },
];
});
it('requires all terms to match for "and" operator', () => {
const filters = {
text: { terms: ['Tiger', 'burning', 'bright'], operator: 'and' },
};
const result = viewFilter.filter(annotations, filters);
assert.deepEqual(result, [1]);
});
it('requires at least one term to match for "or" operator', () => {
const filters = {
text: { terms: ['Tiger', 'quaint'], operator: 'or' },
};
const result = viewFilter.filter(annotations, filters);
assert.equal(result.length, 2);
});
});
describe('"any" field', () => {
it('finds matches in any field', () => {
const annotations = [
{ id: 1, text: poem.tiger },
{ id: 2, user: 'Tiger' },
{ id: 3, tags: ['Tiger'] },
];
const filters = { any: { terms: ['Tiger'], operator: 'and' } };
const result = viewFilter.filter(annotations, filters);
assert.equal(result.length, 3);
});
it('matches if combined fields match "and" query', () => {
const annotation = {
id: 1,
text: poem.tiger,
target: [{
selector: [{
type: 'TextQuoteSelector',
exact: 'The Tiger by William Blake',
}],
}],
user: 'acct:poe@edgar.com',
tags: ['poem', 'Blake', 'Tiger'],
};
// A query which matches the combined fields from the annotation, but not
// individual fields on their own.
const filters = {
any: { terms: ['burning', 'William', 'poem', 'bright'], operator: 'and' },
};
const result = viewFilter.filter([annotation], filters);
assert.equal(result.length, 1);
});
});
describe('"uri" field', () => {
it("matches if the query occurs in the annotation's URI", () => {
const annotation = {
id: 1,
uri: 'https://publisher.org/article',
};
const filters = { uri: { terms: ['publisher'], operator: 'or' } };
const result = viewFilter.filter([annotation], filters);
assert.deepEqual(result, [1]);
});
});
describe('"since" field', () => {
it('matches if the annotation is newer than the query', () => {
const annotation = {
id: 1,
updated: isoDateWithAge(50),
};
const filters = {
since: { terms: [100], operator: 'and' },
};
const result = viewFilter.filter([annotation], filters);
assert.deepEqual(result, [1]);
});
it('does not match if the annotation is older than the query', () => {
const annotation = {
id: 1,
updated: isoDateWithAge(150),
};
const filters = {
since: { terms: [100], operator: 'and' },
};
const result = viewFilter.filter([annotation], filters);
assert.deepEqual(result, []);
});
});
it('ignores filters with no terms in the query', () => {
const annotation = { id: 1, tags: ['foo'] };
const filters = {
any: {
terms: ['foo'],
operator: 'and',
},
tag: {
terms: [],
operator: 'and',
},
};
const result = viewFilter.filter([annotation], filters);
assert.deepEqual(result, [1]);
});
});
...@@ -81,7 +81,7 @@ function viewFilter(unicode) { ...@@ -81,7 +81,7 @@ function viewFilter(unicode) {
* value: a function to extract to facet value for the annotation. * value: a function to extract to facet value for the annotation.
* match: a function to check if the extracted value matches the facet value * match: a function to check if the extracted value matches the facet value
*/ */
this.fields = { const fieldMatchers = {
quote: { quote: {
autofalse: ann => (ann.references || []).length > 0, autofalse: ann => (ann.references || []).length > 0,
value(annotation) { value(annotation) {
...@@ -149,10 +149,10 @@ function viewFilter(unicode) { ...@@ -149,10 +149,10 @@ function viewFilter(unicode) {
if (field === 'any') { if (field === 'any') {
const anyFields = ['quote', 'text', 'tag', 'user']; const anyFields = ['quote', 'text', 'tag', 'user'];
termFilters = terms.map(term => new BinaryOpFilter('or', anyFields.map(field => termFilters = terms.map(term => new BinaryOpFilter('or', anyFields.map(field =>
new TermFilter(field, term, this.fields[field]) new TermFilter(field, term, fieldMatchers[field])
))); )));
} else { } else {
termFilters = terms.map(term => new TermFilter(field, term, this.fields[field])); termFilters = terms.map(term => new TermFilter(field, term, fieldMatchers[field]));
} }
return new BinaryOpFilter(filter.operator, termFilters); return new BinaryOpFilter(filter.operator, termFilters);
}); });
......
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