Skip to content

Commit 198298c

Browse files
committed
update docs
1 parent a30e449 commit 198298c

2 files changed

Lines changed: 302 additions & 0 deletions

File tree

docs/ARCHITECTURE.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,34 @@ For `testar`, the same idea applies one level higher:
192192
- reporting and output ownership belongs to shared session-level managers instead of scattered protocol classes
193193
- SUT-specific customization should live in external settings resources instead of internal protocol subclasses
194194

195+
### Architecture invariants
196+
197+
The following invariants are used when reviewing changes and deciding where new logic belongs:
198+
199+
- `core` does not depend on `engine`, platform modules, `plugin`, `cli`, or `testar`
200+
- `engine` does not hardcode platform heuristics that belong in `windows`, `webdriver`, or `android`
201+
- platform modules provide native implementations but do not orchestrate full sessions
202+
- `plugin` owns session composition and platform/service assembly
203+
- `ComposedProtocol` remains a scriptless runtime orchestrator, not a container for low-level platform logic
204+
- SUT-specific customization should prefer external resources and wrappers before internal module edits
205+
206+
### Where code goes
207+
208+
When deciding where to implement a new change, use this guide:
209+
210+
- new widget rule or eligibility decision
211+
- use a `policy`
212+
- new runtime operation such as capture, derive, select, execute, or evaluate
213+
- use a `service`
214+
- new TESTAR runtime lifecycle hook such as session or sequence behavior
215+
- use a scriptless `capability`
216+
- new session assembly or implementation-selection decision
217+
- use `plugin` composition
218+
- new native platform access or platform-specific low-level behavior
219+
- use the relevant platform module
220+
- new SUT-specific customization
221+
- prefer external wrappers or settings resources when the existing extension seams are sufficient
222+
195223
### Main runtime flow
196224

197225
At the conceptual level, the runtime flow is:
@@ -267,6 +295,10 @@ The design rule is:
267295
- policies
268296
- plans and internal composition stay internal by default
269297

298+
The practical configuration workflow for these seams is documented in:
299+
300+
- `docs/SCRIPTLESS_CONFIGURATION_GUIDE.md`
301+
270302
### Composition resources
271303

272304
`composition.properties` declares wrapper classes for:
@@ -346,6 +378,7 @@ This gives the current architecture a clear boundary:
346378
The loading path is documented in:
347379

348380
- `docs/scriptless/architecture_scriptless_loader_flow.mmd`
381+
- `docs/SCRIPTLESS_CONFIGURATION_GUIDE.md`
349382

350383
## Dialog extension model
351384

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
# Scriptless Configuration Guide
2+
3+
This guide explains how to configure and extend the scriptless TESTAR runtime without editing internal protocol classes.
4+
5+
It focuses on the supported customization seams:
6+
7+
- `test.settings`
8+
- `composition.properties`
9+
- `policies.properties`
10+
11+
## Purpose
12+
13+
Use this guide when you need to answer:
14+
15+
- where should this customization go?
16+
- which file should I edit?
17+
- should I use a policy, a service wrapper, or an identifier-service wrapper?
18+
19+
## Configuration files
20+
21+
### `test.settings`
22+
23+
Use `test.settings` for:
24+
25+
- connector selection
26+
- SUT target settings
27+
- runtime mode settings
28+
- built-in filtering and state-model settings
29+
- selecting:
30+
- `CompositionProfile`
31+
- `CustomCompositionResource`
32+
- `CustomPoliciesResource`
33+
34+
Do not use `test.settings` to declare Java wrapper classes directly.
35+
36+
### `composition.properties`
37+
38+
Use `composition.properties` when you need to wrap or extend:
39+
40+
- services
41+
- scriptless capabilities
42+
43+
Current service-side keys:
44+
45+
- `systemServiceClass`
46+
- `stateServiceClass`
47+
- `stateIdentifierServiceClass`
48+
- `actionDerivationServiceClass`
49+
- `actionIdentifierServiceClass`
50+
- `actionSelectorServiceClass`
51+
- `actionExecutionServiceClass`
52+
- `oracleComposerClass`
53+
54+
Current capability-side keys:
55+
56+
- `settingsCapabilityClass`
57+
- `testSessionCapabilityClass`
58+
- `testSequenceCapabilityClass`
59+
- `stopCriteriaCapabilityClass`
60+
61+
Each class acts as a wrapper around the built-in delegate.
62+
63+
### `policies.properties`
64+
65+
Use `policies.properties` when you need to change widget or state decision rules.
66+
67+
Current policy seams include:
68+
69+
- `clickablePolicies`
70+
- `typeablePolicies`
71+
- `scrollablePolicies`
72+
- `selectablePolicies`
73+
- `enabledPolicies`
74+
- `blockedPolicies`
75+
- `widgetFilterPolicies`
76+
- `visiblePolicies`
77+
- `topLevelPolicies`
78+
79+
Use additive mode when you want to extend built-in behavior.
80+
81+
Use replacement mode when you want to discard the built-in policy family for that seam.
82+
83+
## Where a customization should go
84+
85+
Use this decision guide:
86+
87+
- new rule for whether a widget is clickable, visible, enabled, blocked, or filtered
88+
- use a policy in `policies.properties`
89+
- new behavior around state capture, action derivation, action selection, action execution, or oracle composition
90+
- use a service wrapper in `composition.properties`
91+
- new TESTAR runtime lifecycle behavior such as session hooks, sequence hooks, or stop criteria
92+
- use a capability wrapper in `composition.properties`
93+
- new state or action identification adaptation for SUT-specific or domain-specific needs
94+
- use `stateIdentifierServiceClass` or `actionIdentifierServiceClass`
95+
- new platform-native low-level behavior
96+
- this is internal development work in a platform module, not normal scriptless configuration
97+
98+
## Common recipes
99+
100+
### 1. Add a custom policy
101+
102+
Use this when the customization is a rule such as:
103+
104+
- hide widgets outside the browser canvas
105+
- block denied links
106+
- redefine clickable/typeable eligibility
107+
108+
Steps:
109+
110+
1. Create the Java policy class in the chosen settings workspace.
111+
2. Add the class name to `policies.properties`.
112+
3. Decide whether it should be additive or replacement.
113+
114+
Example:
115+
116+
```properties
117+
visiblePolicies=WebdriverCanvasVisiblePolicy
118+
replaceVisiblePolicies=false
119+
```
120+
121+
Concrete workspace example:
122+
123+
- `testar/resources/settings/webdriver_generic/policies.properties`
124+
- `testar/resources/settings/webdriver_generic/WebdriverCanvasVisiblePolicy.java`
125+
- `testar/resources/settings/webdriver_generic/WebdriverLinkDeniedFilterPolicy.java`
126+
127+
### 2. Wrap a service
128+
129+
Use this when the customization changes runtime behavior rather than a boolean rule.
130+
131+
Examples:
132+
133+
- decorate `StateService`
134+
- decorate `ActionSelectorService`
135+
- decorate `ActionExecutionService`
136+
137+
Steps:
138+
139+
1. Create a wrapper class that accepts the built-in delegate.
140+
2. Register it in `composition.properties`.
141+
3. Keep the wrapper focused on the specific SUT behavior you need.
142+
143+
Example:
144+
145+
```properties
146+
stateServiceClass=MyCustomStateService
147+
```
148+
149+
Concrete workspace examples:
150+
151+
- `testar/resources/settings/webdriver_generic/composition.properties`
152+
- `testar/resources/settings/webdriver_generic/WebdriverParabankTestSequenceLoginCapability.java`
153+
- `testar/resources/settings/webdriver_generic/WebdriverWidgetLeafOverlapOracleComposer.java`
154+
155+
Those two examples show:
156+
157+
- a `testSequenceCapabilityClass` wrapper that performs a SUT-specific login sequence
158+
- an `oracleComposerClass` wrapper that adds a visual overlap oracle on top of the built-in verdict flow
159+
160+
### 3. Adapt state or action identification
161+
162+
Use this when the built-in state or action identification mechanism needs SUT-specific or domain-specific adaptation.
163+
164+
Examples:
165+
166+
- dynamic widget attributes that make states unstable
167+
- action identity that depends on volatile widget data
168+
- domain-specific fields that should dominate state equivalence
169+
- SUT-specific widget attributes that should be normalized before identifiers are built
170+
171+
Steps:
172+
173+
1. Create a `StateIdentifierService` wrapper or `ActionIdentifierService` wrapper.
174+
2. Adapt the widget or action data before delegating to the built-in identifier service.
175+
3. Register the wrapper in `composition.properties`.
176+
177+
Example:
178+
179+
```properties
180+
stateIdentifierServiceClass=WebdriverParabankStateIdentifierService
181+
```
182+
183+
Concrete workspace example:
184+
185+
- `testar/resources/settings/webdriver_generic/WebdriverParabankStateIdentifierService.java`
186+
187+
This seam replaces the old protocol-level customization style for state and action identification.
188+
189+
## Backed example workspace
190+
191+
The shipped `testar/resources/settings/webdriver_generic` workspace now demonstrates the three main customization categories together:
192+
193+
- policy examples in `testar/resources/settings/webdriver_generic/policies.properties`
194+
- service-side identification adaptation in `testar/resources/settings/webdriver_generic/WebdriverParabankStateIdentifierService.java`
195+
- capability and oracle composition in `testar/resources/settings/webdriver_generic/composition.properties`
196+
197+
## What not to customize first
198+
199+
Do not start by editing:
200+
201+
- `ComposedProtocol`
202+
- plan classes
203+
- `PlatformOrchestrator`
204+
- engine internals
205+
206+
unless the supported seams are clearly insufficient.
207+
208+
The preferred order is:
209+
210+
1. settings
211+
2. external policy wrapper
212+
3. external service or capability wrapper
213+
4. internal module change only when the supported seams cannot express the need
214+
215+
## Constructor expectations
216+
217+
Wrapper loading supports delegates first.
218+
219+
Typical service wrapper shape:
220+
221+
```java
222+
public final class MyStateService implements StateService {
223+
224+
private final StateService delegate;
225+
226+
public MyStateService(StateService delegate) {
227+
this.delegate = delegate;
228+
}
229+
}
230+
```
231+
232+
Typical capability wrapper shape:
233+
234+
```java
235+
public final class MyTestSequenceCapability extends TestSequenceCapability {
236+
237+
private final TestSequenceCapability delegate;
238+
239+
public MyTestSequenceCapability(TestSequenceCapability delegate) {
240+
this.delegate = delegate;
241+
}
242+
}
243+
```
244+
245+
Typical identifier wrapper shape:
246+
247+
```java
248+
public final class MyStateIdentifierService implements StateIdentifierService {
249+
250+
private final StateIdentifierService delegate;
251+
252+
public MyStateIdentifierService(StateIdentifierService delegate) {
253+
this.delegate = delegate;
254+
}
255+
}
256+
```
257+
258+
## Practical rule of thumb
259+
260+
If the change answers:
261+
262+
- "is this widget considered X?"
263+
- use a policy
264+
- "how should runtime behavior be extended for this SUT?"
265+
- use a service or capability wrapper
266+
- "how should state or action identification be adapted for this SUT?"
267+
- use an identifier-service wrapper
268+
269+
If the change does not fit any of those seams cleanly, it is probably internal architecture work rather than normal scriptless configuration.

0 commit comments

Comments
 (0)