Skip to content
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

feat: mark and scroll to selected hit when opening Search dialog #179

2 changes: 1 addition & 1 deletion integration/e2e/features/clear-content-search.feature
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Feature: Clear content search
I want to remove highlighting from search results

Scenario: Clear search hits
Given the viewer is opened with a publication with the word "Gjallarhorn" 5 times inside
Given the viewer is opened with a publication with the word "Gjallarhorn" 45 times inside
And the viewer is in dashboard view
And the user search for the word "Gjallarhorn"
And the user selects the first hit
Expand Down
42 changes: 34 additions & 8 deletions integration/e2e/features/content-search.feature
Original file line number Diff line number Diff line change
@@ -1,48 +1,74 @@
@android @iphone @desktop
Feature: Content search
In order to find specific content inside a publication
As a user
I want to search for text in the publication

@desktop @android @iphone
Scenario: Search with hits
Given the viewer is opened with a publication with the word "Gjallarhorn" 5 times inside
Given the viewer is opened with a publication with the word "Gjallarhorn" 45 times inside
And the viewer is in dashboard view
When the user search for the word "Gjallarhorn"
Then there are 5 results found
Then there are 45 results found
And the word "Gjallarhorn" should be highlighted

@desktop @android @iphone
Scenario: Search with no hits
Given the viewer is opened with a publication without the word "Heimdall"
And the viewer is in dashboard view
When the user search for the word "Heimdall"
Then there are no results found

@desktop @android @iphone
Scenario: Go to search hit
Given the viewer is opened with a publication with the word "Gjallarhorn" 5 times inside
Given the viewer is opened with a publication with the word "Gjallarhorn" 45 times inside
And the viewer is in dashboard view
When the user search for the word "Gjallarhorn"
And the user selects the first hit
Then the page with hit number 1 should be displayed

@desktop @android @iphone
Scenario: Go to next search hit
Given the viewer is opened with a publication with the word "Gjallarhorn" 5 times inside
Given the viewer is opened with a publication with the word "Gjallarhorn" 45 times inside
And the viewer is in dashboard view
And the user search for the word "Gjallarhorn"
And the user has selected the second hit
When the user select the next hit button
Then the page with hit number 3 should be displayed

@desktop @android @iphone
Scenario: Go to previous search hit
Given the viewer is opened with a publication with the word "Gjallarhorn" 5 times inside
Given the viewer is opened with a publication with the word "Gjallarhorn" 45 times inside
And the viewer is in dashboard view
And the user search for the word "Gjallarhorn"
And the user has selected the second hit
When the user select the previous hit button
Then the page with hit number 1 should be displayed

@desktop
Scenario: Mark selected hit
Given the viewer is opened with a publication with the word "Gjallarhorn" 5 times inside
Given the viewer is opened with a publication with the word "Gjallarhorn" 45 times inside
And the viewer is in dashboard view
When the user search for the word "Gjallarhorn"
And the user selects the first hit
Then hit number 1 should be marked
Then the hit should be marked

@desktop
Scenario: Mark and scroll to current hit when reopening search dialog
Given the viewer is opened with a publication with the word "Gjallarhorn" 45 times inside
And the viewer is in dashboard view
And the user has search for the word "Gjallarhorn"
And the user has selected the last hit
When the user closes the search dialog
And the user opens the search dialog
Then the hit should be marked
And the hit should be visible

@android @iphone
Scenario: Mark and scroll to current hit when reopening search dialog
Given the viewer is opened with a publication with the word "Gjallarhorn" 45 times inside
And the viewer is in dashboard view
And the user has search for the word "Gjallarhorn"
And the user has selected the last hit
When the user opens the search dialog
Then the hit should be marked
And the hit should be visible
9 changes: 9 additions & 0 deletions integration/e2e/helpers/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import { browser, ElementFinder, protractor } from 'protractor/built';
import { Capabilities } from 'selenium-webdriver';

Expand Down Expand Up @@ -51,4 +52,12 @@ export class Utils {
await browser.sleep(100);
}
}

async isElementVisible(element: ElementFinder) {
return await EC.visibilityOf(element) ? true : false;
}

async isElementInvisible(element: ElementFinder) {
return await EC.invisibilityOf(element) ? true : false;
}
}
32 changes: 21 additions & 11 deletions integration/e2e/pages/content-search.po.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export class ContentSearchPage {
return el.isPresent();
}

closeButton(): Promise<ElementFinder> {
return utils.waitForElement(element(by.css('#close-content-search-dialog-button')));
}

async setSearchTerm(term: string) {
const el: ElementFinder = await utils.waitForElement(element(by.css('.content-search-input')));
await el.clear();
Expand All @@ -26,10 +30,14 @@ export class ContentSearchPage {
return parseInt(numberOfHits, 8);
}

async getHits() {
const el = element.all(by.css('.content-search-container .hit'));
await utils.waitForElement(el.last());
return el;
async getHits(): Promise<any> {
return element.all(by.css('.content-search-container .hit'));
}

async getHit(index: number): Promise<ElementFinder> {
const els = await element.all(by.css('.content-search-container .hit'));
const pagesArray = await els.map((page, i) => page);
return pagesArray[index];
}

contentSearchNavigatorToolbar() {
Expand All @@ -48,13 +56,15 @@ export class ContentSearchPage {
return utils.waitForElement(element(by.css('#footerNavigateNextHitButton')));
}

async isSelected(index: number) {
try {
utils.waitForElement(element(by.css(`.openseadragon-canvas [mimeHitIndex="${index}"][.hit.selected]`)));
return true;
} catch (e) {
return false;
}
async hitIsSelected(index: number) {
const el = await this.getHit(index);
const classes = await el.getAttribute('class');
return classes.indexOf('mat-primary') !== -1;
}

async hitIsVisible(index: number): Promise<boolean> {
const el = await this.getHit(index);
return await utils.isElementVisible(el);
}

async getHighlighted() {
Expand Down
10 changes: 9 additions & 1 deletion integration/e2e/pages/viewer.po.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,15 @@ export class ViewerPage {
if (manifestName) {
uri += '?manifestUri=' + bookShelf[manifestName];
}
await browser.get(uri);

for (let retry = 0; retry < 5; retry++) {
try {
await browser.get(uri, 10000);
break;
} catch (e) {
console.log(`Error connecting to ${uri} (retry ${retry})`, e);
}
}
await browser.sleep(1000);
}
async goToPage(pageNumber: number) {
Expand Down
66 changes: 56 additions & 10 deletions integration/e2e/step-definitions/content-search.steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,26 @@ import { ViewerPage, Point } from '../pages/viewer.po';
defineSupportCode(function ({ Given, When, Then }) {
const page = new ViewerPage();
const contentSearchPage = new ContentSearchPage();
let selectedHitIndex: number;

Given(/^the search dialog is open$/, async () => {
await page.openContentSearchDialog();
});

Given(/^the user has selected the second hit$/, async () => {
await selectHit(1);
Given(/^the user has search for the word "(.*)"$/, async (term: string) => {
await search(term);
});

Given(/^the user has selected the (.*) hit$/, async (hit: string) => {
await selectHit(hit);
});

When(/^the user search for the word "(.*)"$/, async (term: string) => {
await page.openContentSearchDialog();
await contentSearchPage.setSearchTerm(term);
await page.waitForAnimation();
await search(term);
});

When(/^the user selects the first hit$/, async () => {
await selectHit(0);
When(/^the user selects the (.*) hit$/, async (hit: string) => {
await selectHit(hit);
});

When(/^the user select the (.*) hit button$/, async (action: string) => {
Expand All @@ -39,6 +42,17 @@ defineSupportCode(function ({ Given, When, Then }) {
await page.waitForAnimation();
});

When(/^the user closes the search dialog$/, async () => {
const closeButton = await contentSearchPage.closeButton();
await closeButton.click();
await page.waitForAnimation();
});

When(/^the user opens the search dialog$/, async () => {
await page.openContentSearchDialog();
await page.waitForAnimation();
});

Then(/^there are (.*) results found$/, async (numberOfHits: string) => {
const expected = numberOfHits === 'no' ? 0 : parseInt(numberOfHits, 8);
const hits = await contentSearchPage.getNumberOfHits();
Expand Down Expand Up @@ -75,15 +89,47 @@ defineSupportCode(function ({ Given, When, Then }) {
expect(isOpen).to.equal(false);
});

Then(/^hit number (.*) should be marked$/, async (hitIndex: number) => {
const isSelected: boolean = await contentSearchPage.isSelected(hitIndex);
Then(/^the hit should be marked$/, async () => {
const isSelected: boolean = await contentSearchPage.hitIsSelected(selectedHitIndex);
expect(isSelected).to.equal(true);
});

async function selectHit(selected: number) {
Then(/^the hit should be visible$/, async () => {
const isVisible: boolean = await contentSearchPage.hitIsVisible(selectedHitIndex);
expect(isVisible).to.equal(true);
});

async function search(term: string) {
await page.openContentSearchDialog();
await contentSearchPage.setSearchTerm(term);
await page.waitForAnimation();
}

async function selectHit(hit: string) {
const selected = await hitStringToHitIndex(hit);
const hits = await contentSearchPage.getHits();
const first = hits[selected];
await first.click();
await page.waitForAnimation();
selectedHitIndex = selected;
}

async function hitStringToHitIndex(hit: string): Promise<number> {
let index: number;
if ('first' === hit) {
index = 0;
} else if ('second' === hit) {
index = 1;
} else if ('last' === hit) {
const hits = await contentSearchPage.getHits();
index = hits.length - 1;
} else {
try {
index = parseInt(hit, 10);
} catch (e) {
throw new Error(`Unrecognized value "${hit}`);
}
}
return index;
}
});
2 changes: 1 addition & 1 deletion integration/e2e/step-definitions/viewer-page.steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ defineSupportCode(function ({ Given, Then }) {
}
});

Given(/^the viewer is opened with a publication with the word "Gjallarhorn" 5 times inside$/, async () => {
Given(/^the viewer is opened with a publication with the word "Gjallarhorn" 45 times inside$/, async () => {
await page.open();
});

Expand Down
Loading