Repository navigation
TextBox Test Plan
Kinsey Dodd edited this page Sep 14, 2026
·
2 revisions
This document describes the test plan for the TextBox dialogue-box feature, covering:
| Class | Package | Role |
|---|---|---|
TextConfig |
com.csse3200.game.entities.configs |
Plain config POJO loaded from JSON; resolves colour/alignment strings into GDX types |
TextBoxFactory |
com.csse3200.game.entities.factories |
Loads a TextConfig from disk and constructs a TextBoxComponent
|
TextBoxComponent |
com.csse3200.game.components |
Renders a typewriter-effect, paged dialogue box; handles TAB input to skip/advance/dismiss |
Test implementation: JUnit 5, Mockito (mockito-junit-jupiter, mockito-inline), and the project's GameExtension headless-libGDX harness, per the "libGDX and Testing" course material.
- Verify
TextConfigcorrectly parses and falls back on malformed/missing JSON-sourced values (colours, alignment). - Verify
TextBoxFactorynever throws regardless of config-loading outcome, and correctly delegates toFileLoader. - Verify
TextBoxComponent's core interactive behaviour: page defaulting, the typewriter reveal timer, and the three-way TAB interaction (skip / advance page / dismiss). - Establish a safe, headless (no GPU/display) way to exercise UI components that depend on
GdxandServiceLocator.
In scope: unit-level behaviour of the three classes above, using mocked collaborators (FileLoader, RenderService, Gdx.graphics, Gdx.input).
Out of scope:
- Visual/pixel-level correctness of the rendered nine-patch scroll background (
getBackgroundDrawable) — not asserted beyond "doesn't throw." - Real font rendering/metrics beyond what's needed for scene2d layout to run.
- End-to-end/manual GUI testing (see ui.vision note in course material — libGDX can't be tested headless for true visual/functional GUI testing; out of scope for this automated suite).
- Integration with the real
flat-earth-ui.jsonskin asset pipeline beyond it being loadable (see Risks, §6).
-
Runner: JUnit 5 (Jupiter), with
@ExtendWith(GameExtension.class)for any test that touchesGdxor constructs aUIComponentsubclass. -
Headless backend:
GameExtensionboots aHeadlessApplicationand mocksGdx.gl/Gdx.gl20so no real GPU/display is required (safe for CI, e.g. GitHub Actions). -
Mocking: Mockito for collaborators (
FileLoaderviamockito-inlinestatic mocking,RenderService,Graphics,Input). -
State isolation:
ServiceLocator.clear()is called after each test (viaGameExtension.afterEachand/or an explicit@AfterEach) to avoid state leaking between tests. -
Known environment dependency:
UIComponent.skinis aprotected static final Skinloaded once fromflat-earth/skin/flat-earth-ui.jsonviaGdx.files.internal(...)the first time the class is touched. Tests that construct aTextBoxComponentrely on that asset being resolvable from the test run's working directory; this can't be mocked away since it's a static field.
| # | Test | Verifies |
|---|---|---|
| 1 | shouldHaveExpectedDefaultValues |
Default field values match spec (position, chars/sec, sizing, font path, alignment string, pages) |
| 2 | defaultColoursShouldResolveCorrectly |
Default colour strings resolve to Color.BLACK / TAN / BROWN
|
| 3 | defaultAlignmentShouldResolveToCenter |
Default alignment resolves to Align.center
|
| 4 | shouldParseColourWithColorPrefix |
"Color.RED" → Color.RED
|
| 5 | shouldParseColourWithoutColorPrefix |
"BLUE" → Color.BLUE
|
| 6 | shouldParseColourCaseInsensitively |
"color.green" → Color.GREEN
|
| 7 | shouldFallBackToDefaultOnUnknownColourName |
Unknown name falls back to the fallback colour |
| 8 | shouldFallBackToDefaultOnNullColour |
null falls back to the fallback colour |
| 9 | shouldFallBackToDefaultOnBlankColour |
Blank/whitespace falls back to the fallback colour |
| 10 |
shouldParseLeftAlignment / shouldParseRightAlignment
|
"left"/"right" → Align.left/Align.right
|
| 11 | shouldParseCentreSpellingAsCenter |
British spelling "centre" is accepted |
| 12 | shouldParseAlignmentCaseInsensitivelyWithWhitespace |
" RIGHT " → Align.right
|
| 13 |
shouldFallBackToCenterOnUnknownAlignment / OnNullAlignment / OnBlankAlignment
|
Invalid/missing alignment defaults to center |
| # | Test | Verifies |
|---|---|---|
| 1 | shouldNotThrowWhenConfigFailsToLoad |
FileLoader.readClass returning null is handled without throwing (logged error path) |
| 2 | shouldReadConfigFromGivenFilePath |
FileLoader.readClass is called with (TextConfig.class, path) exactly once |
| 3 | shouldNotThrowWhenConfigLoadsSuccessfully |
A valid config leads to component construction/creation without an exception escaping the factory |
| 4 | shouldNotThrowWithNullFontPath |
A config with no font path falls through to the skin's default font without throwing |
| # | Test | Verifies |
|---|---|---|
| 1 | shouldDefaultToSingleEmptyPageWhenPagesIsNull |
null pages → [""]
|
| 2 | shouldDefaultToSingleEmptyPageWhenPagesIsEmpty |
Empty list → [""]
|
| 3 | shouldKeepProvidedPagesWhenNonEmpty |
Non-empty pages are preserved as-is |
| 4 | shouldNotBuildActorsBeforeFirstDraw |
table stays uninitialised until the first draw() call |
| 5 | shouldGraduallyRevealCharactersOverTime |
Revealed character count increases proportionally to elapsed time × chars/sec, capping at full length |
| 8 | shouldNotShowTableWhenCurrentPageIsEmpty |
An empty page keeps the table hidden |
| 9 | firstTabPressShouldInstantlyRevealRestOfPage |
TAB while still typing reveals the full page immediately, without dismissing |
| 10 | tabOnLastFullyRevealedPageShouldDismissBox |
TAB on a fully-revealed, last page sets dismissed = true and hides the table |
| 11 | tabOnFullyRevealedNonLastPageShouldAdvanceToNextPage |
TAB on a fully-revealed, non-last page advances currentPageIndex and resets the typing state for the new page |
| 12 | drawShouldDoNothingOnceDismissed |
Further draw() calls after dismissal are a no-op (no exceptions, state unchanged) |
| Risk | Mitigation / Notes |
|---|---|
UIComponent.skin's static initializer fails if flat-earth/skin/flat-earth-ui.json isn't resolvable in the test run's working directory |
Not mockable (static final); relies on existing project test configuration. If it fails, the error surfaces as ExceptionInInitializerError on first use, not caught by TextBoxFactory's catch (Exception e). |
Tests rely on reflection to read TextBoxComponent's private fields (pages, revealedChars, dismissed, currentPageIndex, lastSourceContent, table) |
Acceptable for a unit-test-only concern since it doesn't change production code; will need updating if those field names change. |
RenderService/ResourceService method signatures assumed from UIComponent/ServiceLocator source (now confirmed: getStage() on RenderService) |
Confirmed against actual source as of this test suite's creation. |
| No true GUI/visual testing of the scroll background artwork | Deliberately out of scope per course guidance (headless can't validate pixels); would need a tool like ui.vision on a real display for that. |
- Add an integration test that runs
TextBoxFactory.createTextBoxagainst a real sample JSON file on the test classpath, rather than a fully mockedFileLoader. - Add a test for
TextBoxComponent.dispose()verifyingcustomFont.dispose()is invoked when a custom font was loaded. - If
entityis ever set on the component, add a test covering the world-to-screen position calculation branch indraw().
Entities and Components
Input Handling
Arrow Selection Wheel Arrow Selection Wheel — Sprint 3