Skip to content

Step by step api clean#179

Merged
matthew-SG merged 3 commits intomainfrom
StepByStepApiCLEAN
Dec 3, 2025
Merged

Step by step api clean#179
matthew-SG merged 3 commits intomainfrom
StepByStepApiCLEAN

Conversation

@Adish-Singh
Copy link
Copy Markdown
Collaborator

@Adish-Singh Adish-Singh commented Dec 3, 2025

Summary
This pull request introduces full text-to-speech (TTS) support to the step-by-step recipe flow using a new SpeechService abstraction and a concrete SystemTTS implementation. In addition, it applies substantial refactoring across interactors, presenters, state objects, and view models to restore clean architectural boundaries and ensure consistent and testable behavior.The PR also resolves several defects, removes obsolete code, and adds a complete test suite for the navbar interactor.

Key Changes

  1. SystemTTS Enhancements
  • Updated synthesize to throw Exception instead of silently failing.
  • Added validation for empty/invalid input.
  • Unified unsupported OS handling by throwing an exception.
  • Ensured calling layers can detect and handle TTS errors.
  1. SpeechService Interface Updates
  • Consolidated to a single method: void synthesize(String text) throws Exception.
  • Removed the previous duplicate method definition.

Step-By-Step Module Refactor and TTS Integration

  1. Controller : Added speak() method to trigger TTS through the interactor.

  2. Interactor:

    • Added SpeechService dependency.
    • Added executeSpeak(StepByStepInputData) for TTS operations.
    • Executes TTS asynchronously in a background thread.
    • Improved validation and error handling.
    • Refactored navigation logic for clarity and correctness.
  3. Presenter:

  • Reworked presenter methods:
  • prepareSuccessView
  • prepareFailView
  • prepareSpeakFailView
  • Ensures error messages and speaking state are properly reflected in the view model.
  1. StepByStepState
  • Removed duplicated fields and methods.
  • Added new fields: isSpeaking, errorMessage
  • Updated documentation and simplified getters/setters.
  1. StepByStepViewModel
  • Unified property change mechanism (firePropertyChanged).
  • Simplified listener management.
  • Removed obsolete methods and duplicate logic.
  1. StepByStepView
  • Removed direct TTS invocation from the view.
  • View now triggers the controller’s speak() method following Clean Architecture.
  • Updated property change handling to support both state updates and error notifications.

Additional Fixes

  1. LikedRecipeListPresenter
  • Replaced outdated method calls:
  • instructions() → getInstructions().
  • Removed redundant TTS initialization.
  • Fixed incorrect presenter reference.
  1. RecipeInstructions (Minor cleanup.)

Tests

  1. LikedRecipeInteractorTest
  • Updated to use getInstructions() instead of the removed instructions() accessor.
  1. Added: Navbar Tests
  • New files:
    - NavbarInteractorTests
    - NavbarMockPresenter
  • Covers:
    - All navigation routes
    - Sequential navigation calls
    - Correct delegation to presenter

Rationale
Brings the step-by-step module back into alignment with Clean Architecture.
Introduces consistent and testable TTS behavior.
Reduces duplication, improves maintainability, and fixes existing defects.
Adds missing tests for key navigation functionality.

Impact
No external API changes outside the step-by-step use case.
All existing UI components remain functional with improved consistency and reliability.

…enter file since navbar does not have anything that implements its output boundary in its use case, i.e. it does not have its own presenter.
…dhere to clean architecture. So, had to change some of the test files as well
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remaining comments which cannot be posted as a review comment to avoid GitHub Rate Limit

checkstyle

🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.imports.RedundantImportCheck> reported by reviewdog 🐶
Redundant import from the same package - use_case.nav_bar.NavbarOutputBoundary.

import use_case.nav_bar.NavbarOutputBoundary;


🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.coding.ExplicitInitializationCheck> reported by reviewdog 🐶
Variable 'lastViewSwitched' explicitly initialized to 'null' (default value for its type).

private String lastViewSwitched = null;


🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.naming.PackageNameCheck> reported by reviewdog 🐶
Name 'use_case.nav_bar' must match pattern '^[a-z]+(.[a-zA-Z_]\w*)*$'.


🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.imports.CustomImportOrderCheck> reported by reviewdog 🐶
Import statement for 'org.junit.jupiter.api.Assertions.*' is in the wrong order. Should be in the 'STATIC' group, expecting not assigned imports on this line.

import static org.junit.jupiter.api.Assertions.*;


🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.imports.ImportOrderCheck> reported by reviewdog 🐶
Wrong order for 'org.junit.jupiter.api.Assertions.*' import.

import static org.junit.jupiter.api.Assertions.*;


🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.imports.AvoidStarImportCheck> reported by reviewdog 🐶
Using the '.' form of import should be avoided - org.junit.jupiter.api.Assertions..

import static org.junit.jupiter.api.Assertions.*;


🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.coding.FinalLocalVariableCheck> reported by reviewdog 🐶
Variable 'instructions' should be declared final.

RecipeInstructions instructions = presenter.lastHandsfreeInput.getInstructions();


🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.coding.IllegalCatchCheck> reported by reviewdog 🐶
Catching 'Exception' is not allowed.


🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.coding.FinalLocalVariableCheck> reported by reviewdog 🐶
Variable 'speechService' should be declared final.


🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.coding.FinalLocalVariableCheck> reported by reviewdog 🐶
Variable 'interactor' should be declared final.

StepByStepInteractor interactor = new StepByStepInteractor(stepPresenter, speechService);


🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.naming.LambdaParameterNameCheck> reported by reviewdog 🐶
Name 'e' must match pattern '^(id)|([a-z][a-z0-9][a-zA-Z0-9]+)$'.

speakButton.addActionListener(e -> stepByStepController.speak());


🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.coding.FinalLocalVariableCheck> reported by reviewdog 🐶
Variable 'propertyName' should be declared final.

String propertyName = evt.getPropertyName();


🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.coding.FinalLocalVariableCheck> reported by reviewdog 🐶
Variable 'newState' should be declared final.

StepByStepState newState = (StepByStepState) evt.getNewValue();


🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.coding.FinalLocalVariableCheck> reported by reviewdog 🐶
Variable 'newState' should be declared final.

StepByStepState newState = (StepByStepState) evt.getNewValue();

assertEquals(1, output.stepNumber());
assertTrue(output.hasNext());
assertFalse(output.hasPrevious());
assertEquals("Heat the oven to 350°F", output.getStepText());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [checkstyle] reported by reviewdog 🐶
Line matches the illegal pattern '[^\p{ASCII}]'.

// Arrange
RecipeInstructions emptyInstructions = new RecipeInstructions(Collections.emptyList());
StepByStepInputData inputData = new StepByStepInputData(emptyInstructions, 0);
RecipeInstructions instructions = new RecipeInstructions(sampleSteps);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.coding.FinalLocalVariableCheck> reported by reviewdog 🐶
Variable 'instructions' should be declared final.

RecipeInstructions emptyInstructions = new RecipeInstructions(Collections.emptyList());
StepByStepInputData inputData = new StepByStepInputData(emptyInstructions, 0);
RecipeInstructions instructions = new RecipeInstructions(sampleSteps);
StepByStepInputData inputData = new StepByStepInputData(instructions, 2);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.coding.FinalLocalVariableCheck> reported by reviewdog 🐶
Variable 'inputData' should be declared final.

assertFalse(mockPresenter.isPresentCalled(), "Present should not be called for empty steps");
assertTrue(mockPresenter.isSuccessViewCalled());

StepByStepOutputData output = mockPresenter.getLastOutputData();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.coding.FinalLocalVariableCheck> reported by reviewdog 🐶
Variable 'output' should be declared final.

// Arrange
RecipeInstructions nullInstructions = new RecipeInstructions(null);
StepByStepInputData inputData = new StepByStepInputData(nullInstructions, 0);
RecipeInstructions instructions = new RecipeInstructions(sampleSteps);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.coding.FinalLocalVariableCheck> reported by reviewdog 🐶
Variable 'instructions' should be declared final.

/**
* Mock speech service for testing
*/
private static class MockSpeechService implements SpeechService {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.design.FinalClassCheck> reported by reviewdog 🐶
Class MockSpeechService should be declared as final.

* Mock speech service for testing
*/
private static class MockSpeechService implements SpeechService {
private boolean synthesizeCalled = false;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.coding.ExplicitInitializationCheck> reported by reviewdog 🐶
Variable 'synthesizeCalled' explicitly initialized to 'false' (default value for its type).

*/
private static class MockSpeechService implements SpeechService {
private boolean synthesizeCalled = false;
private String lastTextSpoken = null;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.coding.ExplicitInitializationCheck> reported by reviewdog 🐶
Variable 'lastTextSpoken' explicitly initialized to 'null' (default value for its type).

private static class MockSpeechService implements SpeechService {
private boolean synthesizeCalled = false;
private String lastTextSpoken = null;
private boolean shouldFail = false;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.coding.ExplicitInitializationCheck> reported by reviewdog 🐶
Variable 'shouldFail' explicitly initialized to 'false' (default value for its type).

@@ -0,0 +1,70 @@
package use_case.nav_bar;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫 [checkstyle] <com.puppycrawl.tools.checkstyle.checks.naming.PackageNameCheck> reported by reviewdog 🐶
Name 'use_case.nav_bar' must match pattern '^[a-z]+(.[a-zA-Z_]\w*)*$'.

Copy link
Copy Markdown
Owner

@matthew-SG matthew-SG left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the changes you made. Since the checkstyle issues appear to be mainly for your tests, I'll approve the merge.

Copy link
Copy Markdown
Collaborator

@taahacodes taahacodes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you added more test cases which is good, please address the checkstyle issues though. I noticed your step by step interactor assumes steps is never empty or null, is this always the case? Because if it is empty or the index is out of range it'll throw before the fail view is reached you should probably add these edge cases in your tests too. Also i wanted to mention in the StepbyStep presenter an and the view model both have different fail views with very similar behaviour it might be better to merge these into a single error path.

Copy link
Copy Markdown
Collaborator

@hussssni hussssni left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the best code I've ever seen

Copy link
Copy Markdown
Collaborator

@hussssni hussssni left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spectacular

@matthew-SG
Copy link
Copy Markdown
Owner

We have three approvals, I'm vetoing it.

@matthew-SG matthew-SG merged commit d23fd82 into main Dec 3, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants