Skip to content

Commit 140caf6

Browse files
Artur-claude
andcommitted
docs: update full-stack-test.md to reflect simplified architecture
Update documentation to reflect all simplifications made through 3 refactoring commits: 1. AbstractFullStackTest base class creation 2. FullStackTestHelper merge into base class 3. NodeRunner replacement with FrontendUtils.executeCommand Changes: - Update Phase 1 to show AbstractFullStackTest instead of separate components - Update test examples to show simplified single-line approach - Add "Architecture Improvements" section documenting refactorings - Update test naming (removed "FullStack" suffix) - Show before/after comparison (63 lines → 33 lines per test) - Document net -436 lines code reduction Documentation now accurately reflects the current simplified state where writing tests requires only extending AbstractFullStackTest and calling assertTypescriptMatchesSnapshot(). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 3d27295 commit 140caf6

File tree

1 file changed

+190
-43
lines changed

1 file changed

+190
-43
lines changed

packages/java/typescript-generator/full-stack-test.md

Lines changed: 190 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,20 @@ Transform the testing approach from separate Java→OpenAPI and OpenAPI→TypeSc
1818

1919
## Progress Status
2020

21-
**Phase 1: Infrastructure** ✅ COMPLETE
22-
- NodeRunner.java ✅
21+
**Phase 1: Infrastructure** ✅ COMPLETE + SIMPLIFIED
22+
- ~~NodeRunner.java~~ → Uses Flow's FrontendUtils.executeCommand
2323
- run-generator.mjs ✅
24-
- FullStackTestHelper.java ✅
24+
- ~~FullStackTestHelper.java~~ → Merged into AbstractFullStackTest
2525
- TypeScriptComparator.java ✅
26+
- **AbstractFullStackTest.java** ✅ (base class with all configuration)
2627

2728
**Phase 2: Convert Tests** 🔄 IN PROGRESS (4/47 converted)
2829

2930
**Transfertypes Plugin (4/8):**
30-
-UUIDFullStackTest (cleaned: removed connect-client.default.ts, endpoints.ts)
31-
-JsonNodeFullStackTest (cleaned: removed connect-client.default.ts, endpoints.ts)
32-
-MultipartFileFullStackTest (cleaned: removed connect-client.default.ts, endpoints.ts)
33-
-PushTypeFullStackTest (cleaned: removed connect-client.default.ts, endpoints.ts)
31+
-UUIDTest (cleaned: removed connect-client.default.ts, endpoints.ts)
32+
-JsonNodeTest (cleaned: removed connect-client.default.ts, endpoints.ts)
33+
-MultipartFileTest (cleaned: removed connect-client.default.ts, endpoints.ts)
34+
-PushTypeTest (cleaned: removed connect-client.default.ts, endpoints.ts)
3435
- ⏳ PageableTest
3536
- ⏳ BarePageableTest
3637
- ⏳ SignalTest (requires SignalsPlugin fix)
@@ -46,13 +47,27 @@ Transform the testing approach from separate Java→OpenAPI and OpenAPI→TypeSc
4647

4748
## Phase 1: Build Full-Stack Testing Infrastructure
4849

49-
### 1.1 Create Node.js Test Runner (Java)
50-
**Location:** `packages/java/typescript-generator/src/test/java/com/vaadin/hilla/parser/testutils/NodeRunner.java`
50+
### 1.1 Create Abstract Base Test Class ✅ SIMPLIFIED
51+
**Location:** `packages/java/typescript-generator/src/test/java/com/vaadin/hilla/parser/testutils/AbstractFullStackTest.java`
5152

52-
- Similar to `GeneratorShellRunner` but for tests
53-
- Execute Node.js scripts with proper working directory
54-
- Capture stdout/stderr for debugging
55-
- Throw exceptions on non-zero exit codes
53+
**Provides all test configuration in one place:**
54+
```java
55+
public abstract class AbstractFullStackTest {
56+
// Executes Java → OpenAPI → TypeScript pipeline
57+
// Compares generated TypeScript to snapshots
58+
// All configuration pre-configured (plugins, classpath, annotations)
59+
60+
protected void assertTypescriptMatchesSnapshot(Class<?>... endpointClasses)
61+
throws Exception;
62+
}
63+
```
64+
65+
**Key simplifications:**
66+
- Uses Flow's `FrontendUtils.executeCommand()` instead of custom NodeRunner
67+
- All helper methods integrated (no separate FullStackTestHelper)
68+
- Pre-configured with all plugins (Backbone, TransferTypes, Model, Nonnull, SubTypes, MultipartFileChecker)
69+
- Extended classpath includes Flux and EndpointSubscription
70+
- Both @Endpoint and @EndpointExposed annotations configured
5671

5772
### 1.2 Create Generator Execution Script (Node.js)
5873
**Location:** `packages/java/typescript-generator/src/test/resources/run-generator.mjs`
@@ -64,20 +79,9 @@ Transform the testing approach from separate Java→OpenAPI and OpenAPI→TypeSc
6479
// Outputs generated files as JSON map
6580
```
6681

67-
### 1.3 Create Full-Stack Test Helper
68-
**Location:** `packages/java/typescript-generator/src/test/java/com/vaadin/hilla/parser/testutils/FullStackTestHelper.java`
69-
70-
```java
71-
public class FullStackTestHelper {
72-
// Execute full pipeline: Java → OpenAPI → TypeScript
73-
public GeneratedFiles executeFullStack(OpenAPI openAPI);
74-
75-
// Compare generated TS to expected snapshots
76-
public void assertTypescriptMatches(GeneratedFiles actual, Path snapshotsDir);
77-
}
78-
```
82+
### 1.3 Create TypeScript Comparison Utilities
83+
**Location:** `packages/java/typescript-generator/src/test/java/com/vaadin/hilla/parser/testutils/TypeScriptComparator.java`
7984

80-
### 1.4 Create TypeScript Comparison Utilities
8185
- Line-by-line diff with clear error messages
8286
- Handle whitespace normalization
8387
- Support for multiple file assertions
@@ -89,21 +93,32 @@ public class FullStackTestHelper {
8993

9094
For each existing test (e.g., `SignalTest.java`):
9195

92-
### 2.1 Update Test Method
93-
**Before:**
96+
### 2.1 Update Test Method ✅ SIMPLIFIED
97+
98+
**Before (old split testing):**
9499
```java
95100
var openAPI = new Parser()
96101
.execute(List.of(SignalEndpoint.class));
97102
helper.executeParserWithConfig(openAPI); // Checks openapi.json
98103
```
99104

100-
**After:**
105+
**After (with AbstractFullStackTest):**
101106
```java
102-
var openAPI = new Parser()
103-
.execute(List.of(SignalEndpoint.class));
104-
helper.executeFullStack(openAPI); // Generates and checks TypeScript
107+
public class SignalTest extends AbstractFullStackTest {
108+
@Test
109+
public void should_GenerateSignalEndpoint() throws Exception {
110+
assertTypescriptMatchesSnapshot(SignalEndpoint.class);
111+
}
112+
}
105113
```
106114

115+
**Benefits:**
116+
- No Parser configuration needed (handled by base class)
117+
- No helper instantiation needed
118+
- Single line test assertion
119+
- All plugins automatically included
120+
- Consistent across all tests
121+
107122
### 2.2 Generate Initial TypeScript Snapshots
108123
**Location pattern:** `src/test/resources/com/vaadin/hilla/parser/.../snapshots/*.ts`
109124

@@ -121,10 +136,10 @@ Run tests in "generate mode" to create initial snapshots:
121136
- **Model tests**: Include model files (e.g., `MyModel.ts`)
122137
- **Subtypes tests**: Include model files with type guards
123138

124-
### 2.3 Update TestHelper Usage
125-
- Remove `executeParserWithConfig()` calls
126-
- Replace with `executeFullStack()` + `assertTypescriptMatches()`
127-
- Keep same test structure and assertions for other aspects
139+
### 2.3 Update Test Structure ✅ SIMPLIFIED
140+
- Extend `AbstractFullStackTest` base class
141+
- Replace entire test implementation with single `assertTypescriptMatchesSnapshot()` call
142+
- All configuration (plugins, classpath, annotations) handled automatically
128143

129144
**Affected files:** ~42 test classes across:
130145
- `plugins/transfertypes/*`
@@ -169,19 +184,25 @@ Ensure each generator plugin has tests:
169184

170185
For each TS test without Java equivalent (e.g., `BasicClient.spec.ts`):
171186

172-
### 4.1 Create Java Test Class
187+
### 4.1 Create Java Test Class ✅ SIMPLIFIED
173188
**Example:** `packages/java/typescript-generator/src/test/java/com/vaadin/hilla/parser/plugins/client/BasicClientTest.java`
174189

175190
```java
176-
@Test
177-
public void should_GenerateBasicClient() {
178-
var openAPI = resourceLoader.loadOpenAPI("BasicClient.json");
179-
var generated = helper.executeFullStack(openAPI);
180-
helper.assertTypescriptMatches(generated,
181-
resourceLoader.getSnapshotsDir());
191+
public class BasicClientTest extends AbstractFullStackTest {
192+
@Test
193+
public void should_GenerateBasicClient() throws Exception {
194+
assertTypescriptMatchesSnapshot(BasicClientEndpoint.class);
195+
}
182196
}
183197
```
184198

199+
**Note:** If you need to load OpenAPI from JSON instead of generating from Java:
200+
```java
201+
// For edge cases where you have OpenAPI JSON but no Java equivalent
202+
var openAPI = resourceLoader.loadOpenAPI("BasicClient.json");
203+
// Then manually call executeFullStack (internal method)
204+
```
205+
185206
### 4.2 Copy OpenAPI Input
186207
**From:** `packages/ts/generator-plugin-client/test/basic/BasicClient.json`
187208
**To:** `packages/java/typescript-generator/src/test/resources/com/vaadin/hilla/parser/plugins/client/BasicClient.json`
@@ -288,6 +309,29 @@ If issues arise:
288309

289310
## Best Practices for Writing Full-Stack Tests
290311

312+
### 0. Use AbstractFullStackTest Base Class ✅
313+
314+
**All tests should extend AbstractFullStackTest:**
315+
```java
316+
public class MyFeatureTest extends AbstractFullStackTest {
317+
@Test
318+
public void should_VerifyMyFeature() throws Exception {
319+
assertTypescriptMatchesSnapshot(MyEndpoint.class);
320+
}
321+
}
322+
```
323+
324+
**The base class provides:**
325+
- ✅ All plugins pre-configured (Backbone, TransferTypes, Model, Nonnull, SubTypes, MultipartFileChecker)
326+
- ✅ Extended classpath with Flux and EndpointSubscription
327+
- ✅ Both @Endpoint and @EndpointExposed annotations
328+
- ✅ Automatic Node.js execution via Flow's FrontendUtils
329+
- ✅ Snapshot comparison with clear error messages
330+
331+
**Test complexity reduction:**
332+
- Before: ~60 lines per test (manual Parser setup, helper usage)
333+
- After: ~15 lines per test (just extend base class and assert)
334+
291335
### 1. Keep Snapshots Minimal and Focused
292336

293337
**DO:**
@@ -342,3 +386,106 @@ src/test/resources/com/vaadin/hilla/parser/plugins/{plugin}/{feature}/
342386
- Model generation and structure
343387
- Polymorphic types (subtypes)
344388
- Nullability annotations
389+
390+
---
391+
392+
## Architecture Improvements and Simplifications
393+
394+
### Refactoring Summary (3 commits)
395+
396+
The initial infrastructure was simplified through 3 major refactorings:
397+
398+
**1. Commit: Create AbstractFullStackTest base class**
399+
- Unified all test configuration in single base class
400+
- Eliminated boilerplate from test files
401+
- Pre-configured all plugins, classpath, and annotations
402+
- Result: -200 lines across test files
403+
404+
**2. Commit: Merge FullStackTestHelper into AbstractFullStackTest**
405+
- Removed unnecessary helper layer
406+
- All functionality integrated into base class
407+
- Cleaner architecture with single responsibility
408+
- Result: -39 lines (FullStackTestHelper removed)
409+
410+
**3. Commit: Replace NodeRunner with Flow's FrontendUtils.executeCommand**
411+
- **Eliminated custom ProcessBuilder implementation entirely**
412+
- **Uses existing Flow infrastructure (FrontendUtils.executeCommand)**
413+
- Platform-aware command execution (Windows/Unix)
414+
- Proper stream handling and error reporting
415+
- Result: **-197 lines (NodeRunner removed)**
416+
417+
### Net Impact
418+
419+
**Code reduction: -436 lines**
420+
- Deleted: 557 lines of boilerplate and custom infrastructure
421+
- Added: 121 lines (AbstractFullStackTest base class)
422+
423+
**Test simplification:**
424+
```java
425+
// Before: 63 lines
426+
public class UUIDFullStackTest {
427+
private final FullStackTestHelper helper = new FullStackTestHelper(getClass());
428+
429+
@Test
430+
public void should_ReplaceUUIDClassWithStringInTypeScript()
431+
throws IOException, URISyntaxException, FullStackExecutionException {
432+
var openAPI = new Parser()
433+
.classPath(Set.of(helper.getTargetDir().toString()))
434+
.endpointAnnotations(List.of(Endpoint.class))
435+
.endpointExposedAnnotations(List.of(EndpointExposed.class))
436+
.addPlugin(new BackbonePlugin())
437+
.addPlugin(new TransferTypesPlugin())
438+
.execute(List.of(UUIDEndpoint.class));
439+
var generated = helper.executeFullStack(openAPI);
440+
helper.assertTypescriptMatches(generated, helper.getSnapshotsDir());
441+
}
442+
}
443+
444+
// After: 33 lines (48% reduction)
445+
public class UUIDTest extends AbstractFullStackTest {
446+
@Test
447+
public void should_ReplaceUUIDClassWithStringInTypeScript() throws Exception {
448+
assertTypescriptMatchesSnapshot(UUIDEndpoint.class);
449+
}
450+
}
451+
```
452+
453+
### Key Architectural Benefits
454+
455+
1. **Reuses existing infrastructure**: No custom ProcessBuilder code, uses Flow's battle-tested FrontendUtils
456+
2. **Single source of truth**: All test configuration in AbstractFullStackTest
457+
3. **Consistent testing**: All tests use same plugins, classpath, and annotations
458+
4. **Easy to maintain**: Changes to configuration only need to happen in one place
459+
5. **Simple to write**: New tests are 3 lines (class + @Test + assertion)
460+
461+
### Files Structure
462+
463+
**Current infrastructure (as of refactoring):**
464+
```
465+
packages/java/typescript-generator/src/test/java/com/vaadin/hilla/parser/testutils/
466+
├── AbstractFullStackTest.java ← All-in-one base class (uses FrontendUtils)
467+
├── TypeScriptComparator.java ← File comparison logic
468+
└── ResourceLoader.java ← Test resource utilities
469+
470+
packages/java/typescript-generator/src/test/resources/
471+
└── run-generator.mjs ← Node.js script (called by AbstractFullStackTest)
472+
```
473+
474+
**Removed files (no longer needed):**
475+
- ~~NodeRunner.java~~ → Flow's FrontendUtils.executeCommand
476+
- ~~FullStackTestHelper.java~~ → Merged into AbstractFullStackTest
477+
478+
### Usage
479+
480+
Writing a new full-stack test is now trivial:
481+
482+
```java
483+
public class MyNewFeatureTest extends AbstractFullStackTest {
484+
@Test
485+
public void should_GenerateMyFeature() throws Exception {
486+
assertTypescriptMatchesSnapshot(MyEndpoint.class);
487+
}
488+
}
489+
```
490+
491+
That's it! No configuration, no setup, no boilerplate.

0 commit comments

Comments
 (0)