Unverified Commit 20c3b5c0 authored by Kyle Keating's avatar Kyle Keating Committed by GitHub

Merge pull request #1448 from hypothesis/remove-unroll

Remove unroll and convert its use cases to use forEach instead
parents 380f6de3 7e43bad0
......@@ -3,7 +3,6 @@
const html = require('../html');
const toResult = require('../../../shared/test/promise-util').toResult;
const unroll = require('../../../shared/test/util').unroll;
const fixture = require('./html-anchoring-fixture.html');
/** Return all text node children of `container`. */
......@@ -320,7 +319,7 @@ describe('HTML anchoring', function() {
container.remove();
});
const testCases = rangeSpecs.map(function(data) {
const testCases = rangeSpecs.map(data => {
return {
range: {
startContainer: data[0],
......@@ -333,9 +332,8 @@ describe('HTML anchoring', function() {
};
});
unroll(
'describes and anchors "#description"',
function(testCase) {
testCases.forEach(testCase => {
it(`describes and anchors ${testCase.description}`, () => {
// Resolve the range descriptor to a DOM Range, verify that the expected
// text was selected.
const range = toRange(container, testCase.range);
......@@ -373,9 +371,8 @@ describe('HTML anchoring', function() {
});
});
return Promise.all(anchored);
},
testCases
);
});
});
describe('When anchoring fails', function() {
const validQuoteSelector = {
......@@ -436,9 +433,8 @@ describe('HTML anchoring', function() {
frame.remove();
});
unroll(
'generates selectors which match the baseline (#name)',
function(fixture) {
fixtures.forEach(fixture => {
it(`generates selectors which match the baseline ${fixture.name}`, () => {
let fixtureHtml = fixture.html;
const annotations = fixture.annotations.rows;
......@@ -471,9 +467,8 @@ describe('HTML anchoring', function() {
});
});
return Promise.all(annotationsChecked);
},
fixtures
);
Promise.all(annotationsChecked);
});
});
});
});
'use strict';
const adder = require('../adder');
const unroll = require('../../shared/test/util').unroll;
function rect(left, top, width, height) {
return { left: left, top: top, width: width, height: height };
......@@ -57,9 +56,15 @@ describe('annotator.adder', function() {
}
context('when Shadow DOM is supported', function() {
unroll(
'creates the adder DOM in a shadow root (using #attachFn)',
function(testCase) {
[
{
attachFn: 'createShadowRoot', // Shadow DOM v0 API
},
{
attachFn: 'attachShadow', // Shadow DOM v1 API
},
].forEach(testCase => {
it(`creates the adder DOM in a shadow root (using ${testCase.attachFn})`, () => {
const adderEl = document.createElement('div');
let shadowEl;
......@@ -83,16 +88,8 @@ describe('annotator.adder', function() {
);
adderEl.remove();
},
[
{
attachFn: 'createShadowRoot', // Shadow DOM v0 API
},
{
attachFn: 'attachShadow', // Shadow DOM v1 API
},
]
);
});
});
});
describe('button handling', function() {
......
......@@ -3,7 +3,6 @@
'use strict';
const unroll = require('../../../shared/test/util').unroll;
const Guest = require('../../guest');
function quoteSelector(quote) {
......@@ -71,9 +70,25 @@ describe('anchoring', function() {
console.warn.restore();
});
unroll(
'should highlight #tag when annotations are loaded',
function(testCase) {
[
{
tag: 'a simple quote',
quotes: ["This has not been a scientist's war"],
},
{
// Known failure with nested annotations that are anchored via quotes
// or positions. See https://github.com/hypothesis/h/pull/3313 and
// https://github.com/hypothesis/h/issues/3278
tag: 'nested quotes',
quotes: [
"This has not been a scientist's war;" +
' it has been a war in which all have had a part',
"scientist's war",
],
expectFail: true,
},
].forEach(testCase => {
it(`should highlight ${testCase.tag} when annotations are loaded`, () => {
const normalize = function(quotes) {
return quotes.map(function(q) {
return simplifyWhitespace(q);
......@@ -97,24 +112,6 @@ describe('anchoring', function() {
normalize(testCase.quotes)
);
});
},
[
{
tag: 'a simple quote',
quotes: ["This has not been a scientist's war"],
},
{
// Known failure with nested annotations that are anchored via quotes
// or positions. See https://github.com/hypothesis/h/pull/3313 and
// https://github.com/hypothesis/h/issues/3278
tag: 'nested quotes',
quotes: [
"This has not been a scientist's war;" +
' it has been a war in which all have had a part',
"scientist's war",
],
expectFail: true,
},
]
);
});
});
});
'use strict';
const unroll = require('../../shared/test/util').unroll;
const observable = require('../util/observable');
const selections = require('../selections');
......@@ -61,15 +59,11 @@ describe('selections', function() {
clock.restore();
});
unroll(
'emits the selected range when #event occurs',
function(testCase) {
fakeDocument.dispatchEvent({ type: testCase.event });
clock.tick(testCase.delay);
assert.calledWith(onSelectionChanged, range);
},
[{ event: 'mouseup', delay: 20 }]
);
it('emits the selected range when mouseup occurs', function() {
fakeDocument.dispatchEvent({ type: 'mouseup' });
clock.tick(20);
assert.calledWith(onSelectionChanged, range);
});
it('emits an event if there is a selection at the initial subscription', function() {
const onInitialSelection = sinon.stub();
......
'use strict';
/**
* Helper for writing parameterized tests.
*
* This is a wrapper around the `it()` function for creating a Mocha test case
* which takes an array of fixture objects and calls it() once for each fixture,
* passing in the fixture object as an argument to the test function.
*
* Usage:
* unroll('should return #output with #input', function (fixture) {
* assert.equal(functionUnderTest(fixture.input), fixture.output);
* },[
* {input: 'foo', output: 'bar'}
* ]);
*
* Based on https://github.com/lawrencec/Unroll with the following changes:
*
* 1. Support for test functions that return promises
* 2. Mocha's `it()` is the only supported test function
* 3. Fixtures are objects rather than arrays
*
* @param {string} description - Description with optional '#key' placeholders
* which are replaced by the values of the corresponding key from each
* fixture object.
* @param {Function} testFn - Test function which can accept either `fixture`
* or `done, fixture` as arguments, where `done` is the callback for
* reporting completion of an async test and `fixture` is an object
* from the `fixtures` array.
* @param {Array<T>} fixtures - Array of fixture objects.
*/
function unroll(description, testFn, fixtures) {
fixtures.forEach(function(fixture) {
const caseDescription = Object.keys(fixture).reduce(function(desc, key) {
return desc.replace('#' + key, String(fixture[key]));
}, description);
it(caseDescription, function(done) {
if (testFn.length === 1) {
// Test case does not accept a 'done' callback argument, so we either
// call done() immediately if it returns a non-Promiselike object
// or when the Promise resolves otherwise
const result = testFn(fixture);
if (typeof result === 'object' && result.then) {
result.then(function() {
done();
}, done);
} else {
done();
}
} else {
// Test case accepts a 'done' callback argument and takes responsibility
// for calling it when the test completes.
testFn(done, fixture);
}
});
});
}
module.exports = {
unroll: unroll,
};
......@@ -3,7 +3,6 @@
const { createElement } = require('preact');
const { shallow } = require('enzyme');
const unroll = require('../../../shared/test/util').unroll;
const fixtures = require('../../test/annotation-fixtures');
const AnnotationHeader = require('../annotation-header');
......@@ -36,30 +35,28 @@ describe('AnnotationHeader', () => {
assert.equal(replyCollapseLink.prop('onClick'), fakeCallback);
});
unroll(
'it should render the annotation reply count',
testCase => {
[
{
replyCount: 0,
expected: '0 replies',
},
{
replyCount: 1,
expected: '1 reply',
},
{
replyCount: 2,
expected: '2 replies',
},
].forEach(testCase => {
it(`it should render the annotation reply count (${testCase.replyCount})`, () => {
const wrapper = createAnnotationHeader({
replyCount: testCase.replyCount,
});
const replyCollapseLink = wrapper.find('.annotation-link');
assert.equal(replyCollapseLink.text(), testCase.expected);
},
[
{
replyCount: 0,
expected: '0 replies',
},
{
replyCount: 1,
expected: '1 reply',
},
{
replyCount: 2,
expected: '2 replies',
},
]
);
});
});
});
describe('timestamps', () => {
......
......@@ -4,13 +4,11 @@ const angular = require('angular');
const events = require('../../events');
const fixtures = require('../../test/annotation-fixtures');
const testUtil = require('../../../shared/test/util');
const util = require('../../directive/test/util');
const annotationComponent = require('../annotation');
const inject = angular.mock.inject;
const unroll = testUtil.unroll;
const draftFixtures = {
shared: { text: 'draft', tags: [], isPrivate: false },
......@@ -917,9 +915,39 @@ describe('annotation', function() {
});
describe('#shouldShowLicense', function() {
unroll(
'returns #expected if #case_',
function(testCase) {
[
{
case_: 'the annotation is not being edited',
draft: null,
group: groupFixtures.open,
expected: false,
},
{
case_: 'the draft is private',
draft: draftFixtures.private,
group: groupFixtures.open,
expected: false,
},
{
case_: 'the group is private',
draft: draftFixtures.shared,
group: groupFixtures.private,
expected: false,
},
{
case_: 'the draft is shared and the group is open',
draft: draftFixtures.shared,
group: groupFixtures.open,
expected: true,
},
{
case_: 'the draft is shared and the group is restricted',
draft: draftFixtures.shared,
group: groupFixtures.restricted,
expected: true,
},
].forEach(testCase => {
it(`returns ${testCase.expected} if ${testCase.case_}`, () => {
const ann = fixtures.publicAnnotation();
ann.group = testCase.group.id;
fakeStore.getDraft.returns(testCase.draft);
......@@ -928,40 +956,8 @@ describe('annotation', function() {
const controller = createDirective(ann).controller;
assert.equal(controller.shouldShowLicense(), testCase.expected);
},
[
{
case_: 'the annotation is not being edited',
draft: null,
group: groupFixtures.open,
expected: false,
},
{
case_: 'the draft is private',
draft: draftFixtures.private,
group: groupFixtures.open,
expected: false,
},
{
case_: 'the group is private',
draft: draftFixtures.shared,
group: groupFixtures.private,
expected: false,
},
{
case_: 'the draft is shared and the group is open',
draft: draftFixtures.shared,
group: groupFixtures.open,
expected: true,
},
{
case_: 'the draft is shared and the group is restricted',
draft: draftFixtures.shared,
group: groupFixtures.restricted,
expected: true,
},
]
);
});
});
});
describe('#authorize', function() {
......@@ -1272,9 +1268,32 @@ describe('annotation', function() {
assert.equal(el[0].querySelector('blockquote').textContent, '<<-&->>');
});
unroll(
'renders hidden annotations with a custom text class (#context)',
function(testCase) {
[
{
context: 'for moderators',
ann: Object.assign(fixtures.moderatedAnnotation({ hidden: true }), {
// Content still present.
text: 'Some offensive content',
}),
textClass: {
'annotation-body is-hidden': true,
'has-content': true,
},
},
{
context: 'for non-moderators',
ann: Object.assign(fixtures.moderatedAnnotation({ hidden: true }), {
// Content filtered out by service.
tags: [],
text: '',
}),
textClass: {
'annotation-body is-hidden': true,
'has-content': false,
},
},
].forEach(testCase => {
it(`renders hidden annotations with a custom text class (${testCase.context})`, () => {
const el = createDirective(testCase.ann).element;
assert.match(
el.find('markdown-view').controller('markdownView'),
......@@ -1282,33 +1301,8 @@ describe('annotation', function() {
textClass: testCase.textClass,
})
);
},
[
{
context: 'for moderators',
ann: Object.assign(fixtures.moderatedAnnotation({ hidden: true }), {
// Content still present.
text: 'Some offensive content',
}),
textClass: {
'annotation-body is-hidden': true,
'has-content': true,
},
},
{
context: 'for non-moderators',
ann: Object.assign(fixtures.moderatedAnnotation({ hidden: true }), {
// Content filtered out by service.
tags: [],
text: '',
}),
textClass: {
'annotation-body is-hidden': true,
'has-content': false,
},
},
]
);
});
});
it('flags the annotation when the user clicks the "Flag" button', function() {
fakeAnnotationMapper.flagAnnotation.returns(Promise.resolve());
......
......@@ -5,7 +5,6 @@ const { createElement } = require('preact');
const ModerationBanner = require('../moderation-banner');
const fixtures = require('../../test/annotation-fixtures');
const unroll = require('../../../shared/test/util').unroll;
const moderatedAnnotation = fixtures.moderatedAnnotation;
......@@ -44,9 +43,40 @@ describe('ModerationBanner', () => {
ModerationBanner.$imports.$restore();
});
unroll(
'displays if user is a moderator and annotation is hidden or flagged',
function(testCase) {
[
{
// Not hidden or flagged and user is not a moderator
test: 'not hidden or flagged and user is not a moderator',
ann: fixtures.defaultAnnotation(),
expectVisible: false,
},
{
test: 'hidden, but user is not a moderator',
ann: {
...fixtures.defaultAnnotation(),
hidden: true,
},
expectVisible: false,
},
{
test: 'not hidden or flagged and user is a moderator',
ann: fixtures.moderatedAnnotation({ flagCount: 0, hidden: false }),
expectVisible: false,
},
{
test: 'flagged but not hidden and the user is a moderator',
ann: fixtures.moderatedAnnotation({ flagCount: 1, hidden: false }),
expectVisible: true,
},
{
// The client only allows moderators to hide flagged annotations but
// an unflagged annotation can still be hidden via the API.
test: 'hidden but not flagged and the user is a moderator',
ann: fixtures.moderatedAnnotation({ flagCount: 0, hidden: true }),
expectVisible: true,
},
].forEach(testCase => {
it(`displays if the annotation is ${testCase.test}`, () => {
const wrapper = createComponent({
annotation: testCase.ann,
});
......@@ -55,39 +85,8 @@ describe('ModerationBanner', () => {
} else {
assert.isFalse(wrapper.exists());
}
},
[
{
// Not hidden or flagged and user is not a moderator
ann: fixtures.defaultAnnotation(),
expectVisible: false,
},
{
// Hidden, but user is not a moderator
ann: {
...fixtures.defaultAnnotation(),
hidden: true,
},
expectVisible: false,
},
{
// Not hidden or flagged and user is a moderator
ann: fixtures.moderatedAnnotation({ flagCount: 0, hidden: false }),
expectVisible: false,
},
{
// Flagged but not hidden
ann: fixtures.moderatedAnnotation({ flagCount: 1, hidden: false }),
expectVisible: true,
},
{
// Hidden but not flagged. The client only allows moderators to hide flagged
// annotations but an unflagged annotation can still be hidden via the API.
ann: fixtures.moderatedAnnotation({ flagCount: 0, hidden: true }),
expectVisible: true,
},
]
);
});
});
it('displays the number of flags the annotation has received', function() {
const ann = fixtures.moderatedAnnotation({ flagCount: 10 });
......
......@@ -2,7 +2,6 @@
const { createElement } = require('preact');
const { shallow } = require('enzyme');
const unroll = require('../../../shared/test/util').unroll;
const ShareAnnotationsPanel = require('../share-annotations-panel');
const SidebarPanel = require('../sidebar-panel');
......@@ -95,9 +94,24 @@ describe('ShareAnnotationsPanel', () => {
});
});
unroll(
'it displays appropriate help text depending on group type',
testCase => {
[
{
groupType: 'private',
introPattern: /Use this link.*with other group members/,
visibilityPattern: /Annotations in the private group.*are only visible to group members/,
},
{
groupType: 'restricted',
introPattern: /Use this link to share these annotations with anyone/,
visibilityPattern: /Anyone using this link may view the annotations in the group/,
},
{
groupType: 'open',
introPattern: /Use this link to share these annotations with anyone/,
visibilityPattern: /Anyone using this link may view the annotations in the group/,
},
].forEach(testCase => {
it('it displays appropriate help text depending on group type', () => {
fakeStore.focusedGroup.returns({
type: testCase.groupType,
name: 'Test Group',
......@@ -115,25 +129,8 @@ describe('ShareAnnotationsPanel', () => {
wrapper.find('.share-annotations-panel').text(),
testCase.visibilityPattern
);
},
[
{
groupType: 'private',
introPattern: /Use this link.*with other group members/,
visibilityPattern: /Annotations in the private group.*are only visible to group members/,
},
{
groupType: 'restricted',
introPattern: /Use this link to share these annotations with anyone/,
visibilityPattern: /Anyone using this link may view the annotations in the group/,
},
{
groupType: 'open',
introPattern: /Use this link to share these annotations with anyone/,
visibilityPattern: /Anyone using this link may view the annotations in the group/,
},
]
);
});
});
describe('web share link', () => {
it('displays web share link in readonly form input', () => {
......
'use strict';
const angular = require('angular');
const unroll = require('../../../shared/test/util').unroll;
describe('BrandingDirective', function() {
let $compile;
......@@ -62,9 +61,8 @@ describe('BrandingDirective', function() {
assert.equal(el[0].style.backgroundColor, '');
});
unroll(
'applies branding to elements',
function(testCase) {
require('./h-branding-fixtures').forEach(testCase => {
it('applies branding to elements', () => {
applyBrandingSettings(testCase.settings);
const el = makeElementWithAttrs(testCase.attrs);
......@@ -84,7 +82,6 @@ describe('BrandingDirective', function() {
testCase.expectedPropValue
);
}
},
require('./h-branding-fixtures')
);
});
});
});
......@@ -2,7 +2,6 @@
const angular = require('angular');
const unroll = require('../../../shared/test/util').unroll;
const util = require('./util');
function testComponent() {
......@@ -35,22 +34,20 @@ describe('hOnTouch', function() {
testEl = util.createDirective(document, 'test', {});
});
unroll(
'calls the handler when activated with a "#event" event',
function(testCase) {
[
{
event: 'touchstart',
},
{
event: 'mousedown',
},
{
event: 'click',
},
].forEach(testCase => {
it(`calls the handler when activated with a "${testCase.event}" event`, () => {
util.sendEvent(testEl[0].querySelector('div'), testCase.event);
assert.equal(testEl.ctrl.tapCount, 1);
},
[
{
event: 'touchstart',
},
{
event: 'mousedown',
},
{
event: 'click',
},
]
);
});
});
});
......@@ -5,7 +5,6 @@ const immutable = require('seamless-immutable');
const storeFactory = require('../index');
const annotationFixtures = require('../../test/annotation-fixtures');
const metadata = require('../../util/annotation-metadata');
const unroll = require('../../../shared/test/util').unroll;
const uiConstants = require('../../ui-constants');
const defaultAnnotation = annotationFixtures.defaultAnnotation;
......@@ -360,14 +359,12 @@ describe('store', function() {
});
describe('#setShowHighlights()', function() {
unroll(
'sets the visibleHighlights state flag to #state',
function(testCase) {
[{ state: true }, { state: false }].forEach(testCase => {
it(`sets the visibleHighlights state flag to ${testCase.state}`, () => {
store.setShowHighlights(testCase.state);
assert.equal(store.getState().viewer.visibleHighlights, testCase.state);
},
[{ state: true }, { state: false }]
);
});
});
});
describe('#updatingAnchorStatus', function() {
......
......@@ -3,8 +3,6 @@
const angular = require('angular');
const immutable = require('seamless-immutable');
const unroll = require('../../../shared/test/util').unroll;
const fixtures = immutable({
annotations: [
{
......@@ -87,9 +85,17 @@ describe('annotation threading', function() {
assert.equal(rootThread.thread(store.getState()).children[0].id, '2');
});
unroll(
'should sort annotations by #mode',
function(testCase) {
[
{
sortKey: 'Oldest',
expectedOrder: ['1', '2'],
},
{
sortKey: 'Newest',
expectedOrder: ['2', '1'],
},
].forEach(testCase => {
it(`should sort annotations by ${testCase.mode}`, () => {
store.addAnnotations(fixtures.annotations);
store.setSortKey(testCase.sortKey);
const actualOrder = rootThread
......@@ -98,16 +104,6 @@ describe('annotation threading', function() {
return thread.annotation.id;
});
assert.deepEqual(actualOrder, testCase.expectedOrder);
},
[
{
sortKey: 'Oldest',
expectedOrder: ['1', '2'],
},
{
sortKey: 'Newest',
expectedOrder: ['2', '1'],
},
]
);
});
});
});
'use strict';
const commands = require('../markdown-commands');
const unroll = require('../../shared/test/util').unroll;
/**
* Convert a string containing '<sel>' and '</sel>' markers
......@@ -84,7 +83,7 @@ describe('markdown commands', function() {
});
describe('block formatting', function() {
const FIXTURES = [
[
{
tag: 'adds formatting to blocks',
input: 'one\n<sel>two\nthree</sel>\nfour',
......@@ -105,19 +104,15 @@ describe('markdown commands', function() {
input: '<sel></sel>',
output: '> <sel></sel>',
},
];
unroll(
'#tag',
function(fixture) {
].forEach(fixture => {
it(fixture.tag, () => {
const output = commands.toggleBlockStyle(
parseState(fixture.input),
'> '
);
assert.equal(formatState(output), fixture.output);
},
FIXTURES
);
});
});
});
describe('link formatting', function() {
......@@ -125,35 +120,31 @@ describe('markdown commands', function() {
return commands.convertSelectionToLink(parseState(text), linkType);
};
unroll(
'converts text to links',
function(testCase) {
[{ selection: 'two' }, { selection: 'jim:smith' }].forEach(testCase => {
it('converts text to links', () => {
const sel = testCase.selection;
const output = linkify('one <sel>' + sel + '</sel> three');
assert.equal(
formatState(output),
'one [' + sel + '](<sel>http://insert-your-link-here.com</sel>) three'
);
},
[{ selection: 'two' }, { selection: 'jim:smith' }]
);
});
});
unroll(
'converts URLs to links',
function(testCase) {
[
{ selection: 'http://foobar.com' },
{ selection: 'https://twitter.com/username' },
{ selection: ' http://example.com/url-with-a-leading-space' },
].forEach(testCase => {
it(`converts URLs to links`, () => {
const sel = testCase.selection;
const output = linkify('one <sel>' + sel + '</sel> three');
assert.equal(
formatState(output),
'one [<sel>Description</sel>](' + sel + ') three'
);
},
[
{ selection: 'http://foobar.com' },
{ selection: 'https://twitter.com/username' },
{ selection: ' http://example.com/url-with-a-leading-space' },
]
);
});
});
it('converts URLs to image links', function() {
const output = linkify(
......
'use strict';
const util = require('../../shared/test/util');
const VirtualThreadList = require('../virtual-thread-list');
const unroll = util.unroll;
describe('VirtualThreadList', function() {
let lastState;
let threadList;
......@@ -105,9 +102,36 @@ describe('VirtualThreadList', function() {
});
});
unroll(
'generates expected state when #when',
function(testCase) {
[
{
when: 'scrollRoot is scrolled to top of list',
threads: 100,
scrollOffset: 0,
windowHeight: 300,
expectedVisibleThreads: idRange(0, 5),
expectedHeightAbove: 0,
expectedHeightBelow: 18800,
},
{
when: 'scrollRoot is scrolled to middle of list',
threads: 100,
scrollOffset: 2000,
windowHeight: 300,
expectedVisibleThreads: idRange(5, 15),
expectedHeightAbove: 1000,
expectedHeightBelow: 16800,
},
{
when: 'scrollRoot is scrolled to bottom of list',
threads: 100,
scrollOffset: 18800,
windowHeight: 300,
expectedVisibleThreads: idRange(89, 99),
expectedHeightAbove: 17800,
expectedHeightBelow: 0,
},
].forEach(testCase => {
it(`generates expected state when ${testCase.when}`, () => {
const thread = generateRootThread(testCase.threads);
fakeScrollRoot.scrollTop = testCase.scrollOffset;
......@@ -134,37 +158,8 @@ describe('VirtualThreadList', function() {
lastState.offscreenLowerHeight,
testCase.expectedHeightBelow
);
},
[
{
when: 'scrollRoot is scrolled to top of list',
threads: 100,
scrollOffset: 0,
windowHeight: 300,
expectedVisibleThreads: idRange(0, 5),
expectedHeightAbove: 0,
expectedHeightBelow: 18800,
},
{
when: 'scrollRoot is scrolled to middle of list',
threads: 100,
scrollOffset: 2000,
windowHeight: 300,
expectedVisibleThreads: idRange(5, 15),
expectedHeightAbove: 1000,
expectedHeightBelow: 16800,
},
{
when: 'scrollRoot is scrolled to bottom of list',
threads: 100,
scrollOffset: 18800,
windowHeight: 300,
expectedVisibleThreads: idRange(89, 99),
expectedHeightAbove: 17800,
expectedHeightBelow: 0,
},
]
);
});
});
it('recalculates when a window.resize occurs', function() {
lastState = null;
......@@ -184,9 +179,17 @@ describe('VirtualThreadList', function() {
});
describe('#setThreadHeight', function() {
unroll(
'affects visible threads',
function(testCase) {
[
{
threadHeight: 1000,
expectedVisibleThreads: idRange(0, 1),
},
{
threadHeight: 300,
expectedVisibleThreads: idRange(0, 4),
},
].forEach(testCase => {
it('affects visible threads', () => {
const thread = generateRootThread(10);
fakeWindow.innerHeight = 500;
fakeScrollRoot.scrollTop = 0;
......@@ -198,18 +201,8 @@ describe('VirtualThreadList', function() {
threadIDs(lastState.visibleThreads),
testCase.expectedVisibleThreads
);
},
[
{
threadHeight: 1000,
expectedVisibleThreads: idRange(0, 1),
},
{
threadHeight: 300,
expectedVisibleThreads: idRange(0, 4),
},
]
);
});
});
});
describe('#detach', function() {
......@@ -228,9 +221,24 @@ describe('VirtualThreadList', function() {
});
describe('#yOffsetOf', function() {
unroll(
'returns #offset as the Y offset of the #nth thread',
function(testCase) {
[
{
nth: 'first',
index: 0,
offset: 0,
},
{
nth: 'second',
index: 1,
offset: 100,
},
{
nth: 'last',
index: 9,
offset: 900,
},
].forEach(testCase => {
it(`returns ${testCase.offset} as the Y offset of the ${testCase.nth} thread`, () => {
const thread = generateRootThread(10);
threadList.setRootThread(thread);
idRange(0, 10).forEach(function(id) {
......@@ -238,24 +246,7 @@ describe('VirtualThreadList', function() {
});
const id = idRange(testCase.index, testCase.index)[0];
assert.equal(threadList.yOffsetOf(id), testCase.offset);
},
[
{
nth: 'first',
index: 0,
offset: 0,
},
{
nth: 'second',
index: 1,
offset: 100,
},
{
nth: 'last',
index: 9,
offset: 900,
},
]
);
});
});
});
});
......@@ -3,8 +3,6 @@
const annotationMetadata = require('../annotation-metadata');
const fixtures = require('../../test/annotation-fixtures');
const unroll = require('../../../shared/test/util').unroll;
const documentMetadata = annotationMetadata.documentMetadata;
const domainAndTitle = annotationMetadata.domainAndTitle;
......@@ -288,23 +286,21 @@ describe('annotation-metadata', function() {
assert.isTrue(annotationMetadata.isPublic(fixtures.publicAnnotation()));
});
unroll(
'returns false if an annotation is not publicly readable',
function(testCase) {
[
{
read: ['acct:someemail@localhost'],
},
{
read: ['something invalid'],
},
].forEach(testCase => {
it('returns false if an annotation is not publicly readable', () => {
const annotation = Object.assign(fixtures.defaultAnnotation(), {
permissions: testCase,
});
assert.isFalse(annotationMetadata.isPublic(annotation));
},
[
{
read: ['acct:someemail@localhost'],
},
{
read: ['something invalid'],
},
]
);
});
});
it('returns false if an annotation is missing permissions', function() {
assert.isFalse(annotationMetadata.isPublic(fixtures.defaultAnnotation()));
......
......@@ -3,38 +3,66 @@
const fixtures = require('../../test/annotation-fixtures');
const uiConstants = require('../../ui-constants');
const tabs = require('../tabs');
const unroll = require('../../../shared/test/util').unroll;
describe('tabs', function() {
describe('tabForAnnotation', function() {
unroll(
'shows annotation in correct tab',
function(testCase) {
[
{
ann: fixtures.defaultAnnotation(),
expectedTab: uiConstants.TAB_ANNOTATIONS,
},
{
ann: fixtures.oldPageNote(),
expectedTab: uiConstants.TAB_NOTES,
},
{
ann: Object.assign(fixtures.defaultAnnotation(), { $orphan: true }),
expectedTab: uiConstants.TAB_ORPHANS,
},
].forEach(testCase => {
it(`shows annotation in correct tab (${testCase.expectedTab})`, () => {
const ann = testCase.ann;
const expectedTab = testCase.expectedTab;
assert.equal(tabs.tabForAnnotation(ann), expectedTab);
},
[
{
ann: fixtures.defaultAnnotation(),
expectedTab: uiConstants.TAB_ANNOTATIONS,
},
{
ann: fixtures.oldPageNote(),
expectedTab: uiConstants.TAB_NOTES,
},
{
ann: Object.assign(fixtures.defaultAnnotation(), { $orphan: true }),
expectedTab: uiConstants.TAB_ORPHANS,
},
]
);
});
});
});
describe('shouldShowInTab', function() {
unroll(
'returns true if the annotation should be shown',
function(testCase) {
[
{
// Anchoring in progress.
anchorTimeout: false,
orphan: undefined,
expectedTab: null,
},
{
// Anchoring succeeded.
anchorTimeout: false,
orphan: false,
expectedTab: uiConstants.TAB_ANNOTATIONS,
},
{
// Anchoring failed.
anchorTimeout: false,
orphan: true,
expectedTab: uiConstants.TAB_ORPHANS,
},
{
// Anchoring timed out.
anchorTimeout: true,
orphan: undefined,
expectedTab: uiConstants.TAB_ANNOTATIONS,
},
{
// Anchoring initially timed out but eventually
// failed.
anchorTimeout: true,
orphan: true,
expectedTab: uiConstants.TAB_ORPHANS,
},
].forEach(testCase => {
it('returns true if the annotation should be shown', () => {
const ann = fixtures.defaultAnnotation();
ann.$anchorTimeout = testCase.anchorTimeout;
ann.$orphan = testCase.orphan;
......@@ -47,40 +75,7 @@ describe('tabs', function() {
tabs.shouldShowInTab(ann, uiConstants.TAB_ORPHANS),
testCase.expectedTab === uiConstants.TAB_ORPHANS
);
},
[
{
// Anchoring in progress.
anchorTimeout: false,
orphan: undefined,
expectedTab: null,
},
{
// Anchoring succeeded.
anchorTimeout: false,
orphan: false,
expectedTab: uiConstants.TAB_ANNOTATIONS,
},
{
// Anchoring failed.
anchorTimeout: false,
orphan: true,
expectedTab: uiConstants.TAB_ORPHANS,
},
{
// Anchoring timed out.
anchorTimeout: true,
orphan: undefined,
expectedTab: uiConstants.TAB_ANNOTATIONS,
},
{
// Anchoring initially timed out but eventually
// failed.
anchorTimeout: true,
orphan: true,
expectedTab: uiConstants.TAB_ORPHANS,
},
]
);
});
});
});
});
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