Skip to content

Commit 4f54260

Browse files
authored
Merge pull request #2153 from Parvinmh/fix/stepOrder
Fix/step order
2 parents 24d94bc + f12785f commit 4f54260

File tree

9 files changed

+134
-158
lines changed

9 files changed

+134
-158
lines changed

cypress/support/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { addCompareSnapshotCommand } from "cypress-visual-regression/dist/comman
44

55
addCompareSnapshotCommand({
66
capture: "fullPage",
7-
errorThreshold: 0.09
7+
errorThreshold: 0.09,
88
});
99

1010
Cypress.Commands.add("nextStep", () => {

src/packages/tour/highlight.cy.ts

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,33 +34,44 @@ context("Highlight", () => {
3434

3535
it("should let user interact with the target element", () => {
3636
cy.window().then((window) => {
37+
const button = window.document.querySelector("#clickable-button");
38+
button.addEventListener("mouseover", () => {
39+
button.textContent = "Hovered";
40+
});
41+
3742
window.introJs
3843
.tour()
3944
.setOptions({
4045
steps: [
41-
{
42-
intro: "step one",
43-
},
46+
{ intro: "step one" },
4447
{
4548
element: "#clickable-button",
4649
intro: "step two",
50+
disableInteraction: false,
4751
},
4852
],
4953
})
5054
.start();
5155

52-
let sp = cy.spy(window, "click");
53-
5456
cy.nextStep();
5557
cy.wait(500);
56-
cy.get(".introjs-tooltiptext").contains("step two");
58+
59+
cy.get(".introjs-tooltiptext").should("contain", "step two");
5760

5861
cy.get(".introjs-helperLayer").realHover();
59-
cy.get("#clickable-button").contains("Hovered");
6062

61-
cy.get(".introjs-helperLayer")
62-
.realClick()
63-
.then(() => expect(sp).to.be.calledOnce);
63+
cy.get("#clickable-button").should("contain", "Hovered");
64+
65+
cy.get("#clickable-button").then(($btn) => {
66+
const spy = cy.spy();
67+
$btn.on("click", spy);
68+
69+
cy.get(".introjs-helperLayer")
70+
.realClick()
71+
.then(() => {
72+
expect(spy).to.have.been.calledOnce;
73+
});
74+
});
6475
});
6576
});
6677

src/packages/tour/navigation.cy.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,15 @@ context("Navigation", () => {
5454
});
5555

5656
it("should navigate by clicking on the bullet items", () => {
57-
cy.get(".introjs-tooltiptext").contains("step one");
58-
cy.get(".introjs-bullets > ul > li:nth-child(1)").click();
59-
cy.get(".introjs-tooltiptext").contains("step one");
60-
cy.get(".introjs-bullets > ul > li:nth-child(2)").click();
61-
cy.get(".introjs-tooltiptext").contains("step two");
57+
cy.get(".introjs-tooltiptext").should("contain.text", "step one");
58+
59+
cy.get(".introjs-bullets > ul > li:nth-child(1)").trigger("click");
60+
cy.get(".introjs-tooltiptext").should("contain.text", "step one");
61+
62+
cy.get(".introjs-bullets > ul > li:nth-child(2)").trigger("click");
63+
64+
cy.get(".introjs-tooltiptext").should(($el) => {
65+
expect($el.text().trim()).to.eq("step two");
66+
});
6267
});
6368
});

src/packages/tour/option.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ describe("getDefaultTourOptions", () => {
3737
translator.translate("messages.stepNumbersOfLabel")
3838
);
3939
expect(opts.dontShowAgainLabel).toBe(
40-
translator.translate("messages.dontShowAgain")
40+
translator.translate("messages.dontShowAgainLabel")
4141
);
4242
expect(opts.language).toEqual("es_ES");
4343
});

src/packages/tour/option.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,9 @@ export function getDefaultTourOptions(translator?: Translator): TourOptions {
118118
disableInteraction: false,
119119

120120
dontShowAgain: false,
121-
dontShowAgainLabel: activeTranslator.translate("messages.dontShowAgain"),
121+
dontShowAgainLabel: activeTranslator.translate(
122+
"messages.dontShowAgainLabel"
123+
),
122124
dontShowAgainCookie: "introjs-dontShowAgain",
123125
dontShowAgainCookieDays: 365,
124126
helperElementPadding: 10,

src/packages/tour/steps.test.ts

Lines changed: 13 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,23 @@ describe("steps", () => {
1818
test("should decrement the step counter", async () => {
1919
// Arrange
2020
const mockTour = getMockTour();
21-
mockTour.setCurrentStep(1);
21+
mockTour.setSteps(getMockSteps());
22+
await mockTour.setCurrentStep(1);
2223

23-
// Act
2424
await previousStep(mockTour);
2525

26-
// Assert
2726
expect(mockTour.getCurrentStep()).toBe(0);
2827
});
2928

3029
test("should not decrement when step is 0", async () => {
3130
// Arrange
3231
const mockTour = getMockTour();
33-
mockTour.setCurrentStep(0);
32+
mockTour.setSteps(getMockSteps());
33+
await mockTour.setCurrentStep(0);
3434

3535
// Act
3636
await previousStep(mockTour);
37+
await Promise.resolve();
3738

3839
// Assert
3940
expect(mockTour.getCurrentStep()).toBe(0);
@@ -44,7 +45,8 @@ describe("steps", () => {
4445
test("should increment the step counter", async () => {
4546
// Arrange
4647
const mockTour = getMockTour();
47-
mockTour.setCurrentStep(0);
48+
mockTour.setSteps(getMockSteps());
49+
await mockTour.setCurrentStep(0);
4850

4951
// Act
5052
await nextStep(mockTour);
@@ -67,77 +69,16 @@ describe("steps", () => {
6769
expect(showElementMock).toHaveBeenCalledTimes(1);
6870
});
6971

70-
test("should call the onBeforeChange callback", async () => {
71-
// Arrange
72-
const mockTour = getMockTour();
73-
mockTour.setSteps(getMockSteps());
74-
const fnBeforeChangeCallback = jest.fn();
75-
mockTour.onBeforeChange(fnBeforeChangeCallback);
76-
77-
// Act
78-
await nextStep(mockTour);
79-
80-
// Assert
81-
expect(fnBeforeChangeCallback).toHaveBeenCalledTimes(1);
82-
expect(fnBeforeChangeCallback).toHaveBeenCalledWith(
83-
undefined,
84-
0,
85-
"forward"
86-
);
87-
});
88-
89-
test("should not continue when onBeforeChange return false", async () => {
90-
// Arrange
91-
const mockTour = getMockTour();
92-
const showElementMock = jest.fn();
93-
(showElement as jest.Mock).mockImplementation(showElementMock);
94-
const fnBeforeChangeCallback = jest.fn();
95-
fnBeforeChangeCallback.mockReturnValue(false);
96-
97-
mockTour.onBeforeChange(fnBeforeChangeCallback);
98-
99-
// Act
100-
await nextStep(mockTour);
101-
102-
// Assert
103-
expect(fnBeforeChangeCallback).toHaveBeenCalledTimes(1);
104-
expect(showElementMock).toHaveBeenCalledTimes(0);
105-
});
106-
107-
test("should wait for the onBeforeChange promise object", async () => {
72+
test("should call the complete callback", async () => {
10873
// Arrange
10974
const mockTour = getMockTour();
110-
mockTour.setSteps(getMockSteps());
111-
const showElementMock = jest.fn();
112-
(showElement as jest.Mock).mockImplementation(showElementMock);
75+
mockTour.setSteps(getMockSteps().slice(0, 2));
11376

114-
const onBeforeChangeMock = jest.fn();
115-
const sideEffect: number[] = [];
116-
117-
mockTour.onBeforeChange(async () => {
118-
return new Promise<boolean>((res) => {
119-
setTimeout(() => {
120-
sideEffect.push(1);
121-
onBeforeChangeMock();
122-
res(true);
123-
}, 50);
124-
});
77+
// Mock isEnd so that after reaching step 1, next call finishes the tour
78+
jest.spyOn(mockTour, "isEnd").mockImplementation(() => {
79+
return mockTour.getCurrentStep() === 1;
12580
});
12681

127-
expect(sideEffect).toHaveLength(0);
128-
129-
// Act
130-
await nextStep(mockTour);
131-
132-
// Assert
133-
expect(sideEffect).toHaveLength(1);
134-
expect(onBeforeChangeMock).toHaveBeenCalledBefore(showElementMock);
135-
});
136-
137-
test("should call the complete callback", async () => {
138-
// Arrange
139-
const mockTour = getMockTour();
140-
mockTour.setSteps(getMockSteps().slice(0, 2));
14182
const fnCompleteCallback = jest.fn();
14283
mockTour.onComplete(fnCompleteCallback);
14384

@@ -148,7 +89,7 @@ describe("steps", () => {
14889

14990
// Assert
15091
expect(fnCompleteCallback).toBeCalledTimes(1);
151-
expect(fnCompleteCallback).toHaveBeenCalledWith(2, "end");
92+
expect(fnCompleteCallback).toHaveBeenCalledWith(1, "end");
15293
});
15394

15495
test("should be able to add steps using addStep()", async () => {

src/packages/tour/steps.ts

Lines changed: 9 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -35,40 +35,16 @@ export type TourStep = {
3535
* @api private
3636
*/
3737
export async function nextStep(tour: Tour) {
38-
tour.incrementCurrentStep();
39-
40-
const currentStep = tour.getCurrentStep();
41-
42-
if (currentStep === undefined) {
43-
return false;
44-
}
45-
46-
const nextStep = tour.getStep(currentStep);
47-
let continueStep: boolean | undefined = true;
48-
49-
continueStep = await tour
50-
.callback("beforeChange")
51-
?.call(
52-
tour,
53-
nextStep && (nextStep.element as HTMLElement),
54-
tour.getCurrentStep(),
55-
tour.getDirection()
56-
);
57-
58-
// if `onBeforeChange` returned `false`, stop displaying the element
59-
if (continueStep === false) {
60-
tour.decrementCurrentStep();
61-
return false;
62-
}
63-
6438
if (tour.isEnd()) {
65-
// check if any callback is defined
6639
await tour.callback("complete")?.call(tour, tour.getCurrentStep(), "end");
6740
await tour.exit();
68-
6941
return false;
7042
}
7143

44+
await tour.incrementCurrentStep();
45+
46+
const currentStep = tour.getCurrentStep();
47+
const nextStep = tour.getStep(currentStep!);
7248
await showElement(tour, nextStep);
7349

7450
return true;
@@ -80,39 +56,16 @@ export async function nextStep(tour: Tour) {
8056
* @api private
8157
*/
8258
export async function previousStep(tour: Tour) {
83-
let currentStep = tour.getCurrentStep();
84-
59+
const currentStep = tour.getCurrentStep();
8560
if (currentStep === undefined || currentStep <= 0) {
8661
return false;
8762
}
8863

89-
tour.decrementCurrentStep();
90-
// update the current step after decrementing
91-
currentStep = tour.getCurrentStep();
92-
93-
if (currentStep === undefined) {
94-
return false;
95-
}
96-
97-
const nextStep = tour.getStep(currentStep);
98-
let continueStep: boolean | undefined = true;
64+
await tour.decrementCurrentStep();
9965

100-
continueStep = await tour
101-
.callback("beforeChange")
102-
?.call(
103-
tour,
104-
nextStep && (nextStep.element as HTMLElement),
105-
tour.getCurrentStep(),
106-
tour.getDirection()
107-
);
108-
109-
// if `onBeforeChange` returned `false`, stop displaying the element
110-
if (continueStep === false) {
111-
tour.incrementCurrentStep();
112-
return false;
113-
}
114-
115-
await showElement(tour, nextStep);
66+
const newStep = tour.getCurrentStep()!;
67+
const prevStep = tour.getStep(newStep);
68+
await showElement(tour, prevStep);
11669

11770
return true;
11871
}

src/packages/tour/tour.test.ts

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ import {
1010
waitFor,
1111
} from "../../../tests/jest/helper";
1212
import * as dontShowAgain from "./dontShowAgain";
13-
import { appendMockSteps, getMockPartialSteps, getMockTour } from "./mock";
13+
import {
14+
appendMockSteps,
15+
getMockPartialSteps,
16+
getMockTour,
17+
getMockSteps,
18+
} from "./mock";
1419
import { Tour } from "./tour";
1520
import { helperLayerClassName, overlayClassName } from "./classNames";
1621
import {
@@ -734,4 +739,49 @@ describe("Tour", () => {
734739
}
735740
}
736741
});
742+
743+
describe("setCurrentStep", () => {
744+
test("should call beforeChange and proceed when callback returns true", async () => {
745+
const mockTour = getMockTour();
746+
mockTour.setSteps(getMockSteps());
747+
748+
const fnBeforeChange = jest.fn().mockResolvedValue(true);
749+
mockTour.onBeforeChange(fnBeforeChange);
750+
751+
// Act
752+
await mockTour.setCurrentStep(1);
753+
754+
// Assert
755+
expect(fnBeforeChange).toHaveBeenCalledTimes(1);
756+
expect(mockTour.getCurrentStep()).toBe(1);
757+
});
758+
759+
test("should allow backward step even if callback returns false", async () => {
760+
const mockTour = getMockTour();
761+
mockTour.setSteps(getMockSteps());
762+
mockTour.setCurrentStep(1);
763+
764+
const fnBeforeChange = jest.fn().mockResolvedValue(false);
765+
mockTour.onBeforeChange(fnBeforeChange);
766+
767+
// Act
768+
await mockTour.setCurrentStep(0);
769+
770+
// Assert
771+
expect(fnBeforeChange).toHaveBeenCalledTimes(1);
772+
expect(mockTour.getCurrentStep()).toBe(0);
773+
});
774+
775+
test("should return early if target step does not exist", async () => {
776+
const mockTour = getMockTour();
777+
mockTour.setSteps(getMockSteps());
778+
779+
// Act
780+
const result = await mockTour.setCurrentStep(999);
781+
782+
// Assert
783+
expect(result).toBe(mockTour);
784+
expect(mockTour.getCurrentStep()).toBe(undefined);
785+
});
786+
});
737787
});

0 commit comments

Comments
 (0)