-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Query element by selector later if it's not exists during configuration #1679
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
base: master
Are you sure you want to change the base?
Changes from all commits
33b6136
7112475
fc3cdfd
a0957d7
a8f9435
5f58f8d
6d37653
87fa604
2273565
7580560
33e560b
d5eaf0d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/** | ||
* Waits until Element will appear | ||
* | ||
* @api private | ||
* @method _waitForElement | ||
* @param {string} elSelector Selector to locate Element | ||
* @param {() => void} callback Callback to be called after Element appearance | ||
*/ | ||
export default function _waitForElement(elSelector, callback) { | ||
if (document.querySelector(elSelector) !== null) { | ||
callback(); | ||
return; | ||
} | ||
|
||
if (typeof MutationObserver !== "undefined") { | ||
const observer = new MutationObserver(() => { | ||
if (document.querySelector(elSelector) !== null) { | ||
observer.disconnect(); | ||
callback(); | ||
} | ||
}); | ||
|
||
observer.observe(document.body, { | ||
childList: true, | ||
subtree: true, | ||
attributes: false, | ||
characterData: false, | ||
}); | ||
} else { | ||
// Old browsers will wait by timeout | ||
_waitForElementByTimeout(elSelector, callback, 1000, 10000); | ||
} | ||
} | ||
|
||
/** | ||
* @param {string} elSelector | ||
* @param {() => void} callback | ||
* @param {number} checkInterval In milliseconds | ||
* @param {number} maxTimeout In milliseconds | ||
*/ | ||
export function _waitForElementByTimeout( | ||
elSelector, | ||
callback, | ||
checkInterval, | ||
maxTimeout | ||
) { | ||
let startTimeInMs = Date.now(); | ||
(function loopSearch() { | ||
if (document.querySelector(elSelector) !== null) { | ||
callback(); | ||
return; | ||
} else { | ||
setTimeout(function () { | ||
if (Date.now() - startTimeInMs > maxTimeout) { | ||
callback(); | ||
return; | ||
} | ||
loopSearch(); | ||
}, checkInterval); | ||
} | ||
})(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ describe("fetchIntroSteps", () => { | |
steps: [ | ||
{ | ||
element: "#element_does_not_exist", | ||
position: "top", | ||
intro: "hello world", | ||
}, | ||
{ | ||
|
@@ -23,7 +24,7 @@ describe("fetchIntroSteps", () => { | |
|
||
expect(steps.length).toBe(2); | ||
|
||
expect(steps[0].position).toBe("floating"); | ||
expect(steps[0].position).toBe("top"); | ||
expect(steps[0].intro).toBe("hello world"); | ||
expect(steps[0].step).toBe(1); | ||
|
||
|
@@ -80,11 +81,44 @@ describe("fetchIntroSteps", () => { | |
expect(steps[1].intro).toBe("second"); | ||
expect(steps[1].step).toBe(2); | ||
|
||
expect(steps[2].position).toBe("floating"); | ||
expect(steps[2].position).toBe("bottom"); | ||
expect(steps[2].intro).toBe("third"); | ||
expect(steps[2].step).toBe(3); | ||
}); | ||
|
||
test("should throw an error on calling step.element if it is not exists yet and return element after adding", () => { | ||
const targetElement = document.createElement("div"); | ||
const elId = "later_added"; | ||
const steps = fetchIntroSteps.call( | ||
{ | ||
_options: { | ||
tooltipPosition: "bottom", | ||
steps: [ | ||
{ | ||
element: "#" + elId, | ||
}, | ||
], | ||
}, | ||
}, | ||
targetElement | ||
); | ||
|
||
expect(steps.length).toBe(1); | ||
|
||
try { | ||
const element = steps[0].element; // jshint ignore:line | ||
} catch (e) { | ||
if (!e.message.includes("There is no element with given selector:")) | ||
throw e; | ||
} | ||
Comment on lines
+108
to
+113
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we checking here to make sure the steps array is empty? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, we calling here |
||
|
||
const laterAdded = document.createElement("div"); | ||
laterAdded.setAttribute("id", elId); | ||
document.body.appendChild(laterAdded); | ||
|
||
expect(steps[0].element).toBe(laterAdded); | ||
}); | ||
|
||
test("should find the data-* elements from the DOM", () => { | ||
const targetElement = document.createElement("div"); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
context("Added later element", () => { | ||
const addedLaterElId = "later_added"; | ||
const stepOneText = "step one"; | ||
const stepTwoText = "step two, click on create btn"; | ||
const stepThreeText = "added later element"; | ||
const createDivBtnSelector = "#create-div-button"; | ||
beforeEach(() => { | ||
cy.visit("./cypress/setup/create_div_btn.html").then((window) => { | ||
window | ||
.introJs() | ||
.setOptions({ | ||
disableInteraction: false, | ||
steps: [ | ||
{ | ||
intro: stepOneText, | ||
}, | ||
{ | ||
intro: stepTwoText, | ||
element: createDivBtnSelector, | ||
}, | ||
{ | ||
intro: stepThreeText, | ||
element: "#" + addedLaterElId, | ||
}, | ||
], | ||
}) | ||
.start(); | ||
}); | ||
}); | ||
|
||
it("should find by selector and highlight added later element", () => { | ||
cy.get(".introjs-tooltiptext").contains(stepOneText); | ||
cy.nextStep(); | ||
cy.get(".introjs-tooltiptext").contains(stepTwoText); | ||
cy.wait(500); | ||
cy.get(createDivBtnSelector).click(); | ||
cy.nextStep(); | ||
cy.wait(500); | ||
cy.get("#" + addedLaterElId) | ||
.filter(".introjs-showElement") | ||
.contains("Later added div"); | ||
cy.wait(2000); | ||
cy.compareSnapshot("added-later-element-end", 0.05); | ||
cy.doneButton(); | ||
cy.get(".introjs-showElement").should("not.exist"); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,6 +47,14 @@ context("Navigation", () => { | |
cy.get(".introjs-showElement").should("not.exist"); | ||
}); | ||
|
||
it("should exit the tour after right btn pressed at the end", () => { | ||
cy.get(".introjs-tooltiptext").contains("step one"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Beautiful! Thank you There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In fact, there wasn't such error in master before adding this feature. Possible error was taken into account in 87fa604 |
||
cy.nextStep(); | ||
cy.get(".introjs-tooltiptext").contains("step two"); | ||
cy.realPress("ArrowRight"); | ||
cy.get(".introjs-showElement").should("not.exist"); | ||
}); | ||
|
||
it("should close the tour after clicking on the exit button", () => { | ||
cy.get(".introjs-showElement").should("exist"); | ||
cy.get(".introjs-skipbutton").click(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it important to have this object when the DOMElement doesn't exist? Can we just replace it with
undefined
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, my first (local) implementation was with
undefined
, but it could be more changes in other code, that calls.element
.I'll try to do with
undefined
.