/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { describe, it, expect } from "vitest" import { advance, createEmptyVisualInkState, getActiveSection, hideAllVisibleSections, hideOlderLines, makeChoice } from "./visible-state" import { Compiler } from "inkjs" import storyText from "../test/test-story.ink?raw" import { isSectionComplete } from "./story-section" import { InkStory } from "../inkTypes" describe("advance", () => { it("should advance the story normally", () => { const story = new Compiler(storyText).Compile() const visualState = createEmptyVisualInkState() const newVisualInkState = advance(story, visualState) expect(newVisualInkState !== visualState).toBe(true) expect(newVisualInkState.sections !== visualState.sections).toBe(true) expect(newVisualInkState.sections.length).toEqual(1) expect(newVisualInkState.sections[0].activeLine).toEqual(0) const secondVisualInkState = advance(story, newVisualInkState) expect(newVisualInkState !== secondVisualInkState).toBe(true) expect(secondVisualInkState.sections !== newVisualInkState.sections).toBe(true) expect(secondVisualInkState.sections[0].lines).toBe(newVisualInkState.sections[0].lines) expect(secondVisualInkState.sections[0].activeLine).toEqual(1) }) it("should not advance the story if we've already advanced too far", () => { const story = new Compiler(storyText).Compile() let visualState = createEmptyVisualInkState() while (!isSectionComplete(getActiveSection(visualState))) { visualState = advance(story, visualState) } const newVisualState = advance(story, visualState) expect(visualState).toBe(newVisualState) expect(newVisualState.sections.length).toEqual(1) }) }) describe("makeChoice", () => { it("should handle the case where you make a choice before seeing the whole section", () => { const story = new Compiler(storyText).Compile() const emptyState = createEmptyVisualInkState() // make choice should throw an error. expect(() => makeChoice(story, emptyState, 0)).toThrowError("choice out of range") const newVisualState = advance(story, emptyState) expect(newVisualState.sections.length).toBe(1) expect(newVisualState.sections[0].activeLine).toBe(0) }) it("should make choices once you've advanced all the way", () => { const story = new Compiler(storyText).Compile() let visualState = createEmptyVisualInkState() while (!isSectionComplete(getActiveSection(visualState))) { visualState = advance(story, visualState) } const newVisualState = makeChoice(story, visualState, 0) expect(newVisualState.sections[0].activeChoiceIndex).toBe(0) }) it("should load in new sections if you advance after a choice", () => { const story = new Compiler(storyText).Compile() let visualState = createEmptyVisualInkState() while (!isSectionComplete(getActiveSection(visualState))) { visualState = advance(story, visualState) } let newVisualState = makeChoice(story, visualState, 0) expect(newVisualState.sections[0].activeChoiceIndex).toBe(0) newVisualState = advance(story, newVisualState); expect(newVisualState.sections.length).toBe(2); expect(newVisualState.sections[1].activeLine).toBe(0); expect(newVisualState.sections[1].activeChoiceIndex).toBeUndefined(); }) it("should error out if you make an invalid choice", () => { const story = new Compiler(storyText).Compile() let visualState = createEmptyVisualInkState() while (!isSectionComplete(getActiveSection(visualState))) { visualState = advance(story, visualState) } expect(visualState.sections[0].choices.length).toBe(2) expect(() => makeChoice(story, visualState, 3)).toThrowError("choice out of range") }) }) describe("hideAllVisibleSections", () => { it("should hide any visible sections", () => { const story = new Compiler(storyText).Compile() let visualState = createEmptyVisualInkState() while (!isSectionComplete(getActiveSection(visualState))) { visualState = advance(story, visualState) } visualState = makeChoice(story, visualState, 0) visualState = advance(story, visualState) const newVisualInkState = hideAllVisibleSections(visualState) expect(visualState !== newVisualInkState).toBe(true) expect(visualState.sections.length).toEqual(newVisualInkState.sections.length) for (let i = 0; i < newVisualInkState.sections.length - 1; i++) { expect(visualState.sections[i] !== newVisualInkState.sections[i]) expect(visualState.sections[i].isHidden).toBeFalsy() expect(visualState.sections[i].lines).toBe(newVisualInkState.sections[i].lines) expect(newVisualInkState.sections[i].isHidden).toBeTruthy() } // the last section isn't hidden, but rather the lines are const lastIndex = newVisualInkState.sections.length - 1 expect(newVisualInkState.sections[lastIndex].isHidden).toBeFalsy(); expect(visualState.sections[lastIndex].lines !== newVisualInkState.sections[lastIndex].lines).toBe(true) for (let i = 0; i < newVisualInkState.sections[lastIndex].lines.length; i++) { const line = newVisualInkState.sections[lastIndex].lines[i] if (i <= newVisualInkState.sections[lastIndex].activeLine!) { expect(line.isHidden).toBeTruthy() } else { // make sure the line definition didn't change expect(line).toBe(visualState.sections[lastIndex].lines[i]) } } // now if we advance, the newly advanced line should not be hidden const advancedVisualInkState = advance(story, newVisualInkState) for (let i = 0; i < advancedVisualInkState.sections.length - 1; i++) { // hidden status didn't change from first hide, so the section should be the same object expect(newVisualInkState.sections[i]).toBe(advancedVisualInkState.sections[i]) expect(newVisualInkState.sections[i].lines).toBe(advancedVisualInkState.sections[i].lines) expect(advancedVisualInkState.sections[i].isHidden).toBeTruthy() } expect(advancedVisualInkState.sections[lastIndex].isHidden).toBeFalsy(); expect(newVisualInkState.sections[lastIndex] !== advancedVisualInkState.sections[lastIndex]).toBe(true) const newLines = newVisualInkState.sections[lastIndex].lines const newActiveLine = newVisualInkState.sections[lastIndex].activeLine const advancedLines = advancedVisualInkState.sections[lastIndex].lines const advancedActiveLine = advancedVisualInkState.sections[lastIndex].activeLine expect(newActiveLine! + 1).toEqual(advancedActiveLine) expect(newLines[newActiveLine!].isHidden).toBeTruthy() expect(advancedLines[newActiveLine!].isHidden).toBeTruthy() expect(advancedLines[advancedActiveLine!].isHidden).toBeFalsy() }) }) describe("hideOlderLines", () => { const advanceAStoryPastTheChoice = (story: InkStory) => { let visualState = createEmptyVisualInkState() while (!isSectionComplete(getActiveSection(visualState))) { visualState = advance(story, visualState) } visualState = makeChoice(story, visualState, 0) visualState = advance(story, visualState) return visualState } it("should hide the whole section if you don't show enough lines to reach it", () => { const story = new Compiler(storyText).Compile() let visualState = advanceAStoryPastTheChoice(story) let newVisualState = hideOlderLines(visualState, 1) expect(newVisualState.sections[0].isHidden).toBeTruthy() expect(newVisualState.sections !== visualState.sections).toBe(true) // since this section didn't change, expect it to be the same. expect(newVisualState.sections[1]).toBe(visualState.sections[1]) }) })