Using scripter to test the user interfaces

BlScripter Object << #BlScripter traits: {TBlDevScripterActionStep + TBlDevScripterCheckStepCreation}; slots: { #element . #space . #events . #rootStep . #eventHandler . #maxPulseElapsedTime }; tag: 'Scripter'; package: 'Bloc-Scripter' is the framework for scripting and verifying UI interactions in Glamorous Toolkit. It lets users create reproducible scenarios that simulate user actions (clicks, typing, keyboard shortcuts, scrolling) and assert UI state.

The main entry point is BlScripter Object << #BlScripter traits: {TBlDevScripterActionStep + TBlDevScripterCheckStepCreation}; slots: { #element . #space . #events . #rootStep . #eventHandler . #maxPulseElapsedTime }; tag: 'Scripter'; package: 'Bloc-Scripter' . Steps are the building blocks: they model actions or assertions. The scripter manages a BlSpace Object << #BlSpace traits: {TBlEventTarget + TBlSpaceProperties + TBlDebug}; slots: { #id . #host . #hostSpace . #extent . #position . #root . #resizable . #borderless . #dirtyAreas . #eventDispatcher . #eventListener . #eventRecorder . #mouseProcessor . #focusProcessor . #keyboardProcessor . #focusChain . #dragboard . #nextPulseRequested . #currentCursor . #session . #focused . #title . #fullscreen . #fullsize . #layoutError . #tasks . #time . #touchProcessor . #frame . #gestureProcessor . #elementsNeedingPaint . #elementsNeedingLayout . #telemetry . #reference . #elementsNeedingStyle . #elementsNeedingPropertiesComputation . #iconStencil }; sharedVariables: { #UniqueIdGenerator . #UserFontScaleFactor . #UserScaleFactor }; tag: 'Space'; package: 'Bloc' internally — the UI is live and pulsed between steps to let layout, animations, and tasks settle.

Every scripter example follows this structure:

1. Create a scripter instance

newScripter
	<gtExample>
	<return: #BlScripter>
	^ BlScripter new
    

2. Set the root UI element

scripterWithElement
	<gtExample>
	<return: #BlScripter>
	| aScripter |
	aScripter := self newScripter.
	aScripter element: self containerWithRectangle.
	^ aScripter
    

3. Add action steps (simulating user interactions)

clickOnButton
	"Demonstrates clicking a button by element ID.
	Uses clickStep: which auto-plays the step."
	<gtExample>
	| aScripter |
	aScripter := self scripterWithButton.
	aScripter clickStep: 
			[:s |
			s
				label: 'Click the test button';
				id: #testButton].
	^aScripter
    

4. Add assertion/check steps (verifying UI state)

clickOnButtonAndAssert
	"Demonstrates clicking and then asserting the UI changed.
	Shows the pattern: action step followed by assertion step."
	<gtExample>
	| aScripter |
	aScripter := self clickOnButton.
	aScripter assertStep: 
			[:s |
			s
				label: 'Assert button still exists';
				exists;
				id: #testButton].
	^aScripter
    

5. (Optional) Set a model on the scripter, accessible from steps via #selector onModel self target: (BlDevScripterModelTarget new)

scripterWithModel
	"Demonstrates setting a model on scripter and asserting on it."
	<gtExample>
	<return: #BlScripter>
	| aScripter |
	aScripter := self newScripter.
	aScripter model: 42.
	aScripter element: self containerWithRectangle.
	aScripter assertStep: [ :s |
		s
			label: 'Assert model is 42';
			value: [ :aModel | aModel ] equals: [ 42 ];
			onModel ].
	^ aScripter
    

6. Return the scripter (for composition by other examples)

Overall, BlScripterExamples>>#exampleClickButton exampleClickButton "Demonstrates the full scripter pattern: create, set element, act, assert." <gtExample> | aScripter | aScripter := BlScripter new. aScripter element: (BrFrame new matchParent; addChild: (BlElement new id: #myButton; size: 50 @ 50; addEventHandlerOn: BlClickEvent do: [ :e | e currentTarget background: Color green ])). aScripter clickStep: [ :s | s id: #myButton ]. aScripter assertStep: [ :s | s label: 'Assert background is green'; satisfies: [ :el | el background paint color = Color green ]; id: #myButton ]. ^ aScripter shows all these steps using the raw API (without the TBlDevScripterExamples Trait << #TBlDevScripterExamples tag: 'Scripter'; package: 'Bloc-Scripter' trait, to show the minimal setup).

exampleClickButton
	"Demonstrates the full scripter pattern: create, set element, act, assert."
	<gtExample>
	| aScripter |
	aScripter := BlScripter new.
	aScripter element: (BrFrame new
		matchParent;
		addChild: (BlElement new
			id: #myButton;
			size: 50 @ 50;
			addEventHandlerOn: BlClickEvent do: [ :e |
				e currentTarget background: Color green ])).
	aScripter clickStep: [ :s | s id: #myButton ].
	aScripter assertStep: [ :s |
		s
			label: 'Assert background is green';
			satisfies: [ :el | el background paint color = Color green ];
			id: #myButton ].
	^ aScripter
    

A step (subclass of BlDevScripterStep Object << #BlDevScripterStep slots: { #scripter . #state . #label . #properties . #maxPulseElapsedTime }; tag: 'Scripter-Steps'; package: 'Bloc-Scripter' ) is the fundamental unit of work in a scripter script.

Every step holds a target specification (built via TBlDevScripterTarget Trait << #TBlDevScripterTarget tag: 'Scripter'; package: 'Bloc-Scripter' ) that locates the element to act upon or assert against, and implements BlDevScripterStep>>#playOn: playOn: aScripter to execute its logic against the scripter.

Steps are composed into trees: composite steps (BlDevScripterCompositeStep BlDevScripterStep << #BlDevScripterCompositeStep traits: {TBlDevScripterCompositeStepTarget + TBlDevScripterTarget}; slots: { #steps }; tag: 'Scripter-Steps'; package: 'Bloc-Scripter' ) group child steps into logical sequences, while leaf steps represent individual atomic interactions or assertions.

When played, a step fires events through the space's simulation API, then the scripter pulses the space so that event handling, layout, and rendering settle before the next step executes.

There are several types of steps like:

- BlDevScripterInteractionStep BlDevScripterTargetedStep << #BlDevScripterInteractionStep slots: {}; tag: 'Scripter-Steps'; package: 'Bloc-Scripter' : high-level UI interactions (click, double-click, type text, fire events, request focus, shortcuts, wait) defined by subclasses

- BlDevScripterKeyboardStep BlDevScripterStep << #BlDevScripterKeyboardStep slots: { #key . #modifiers }; tag: 'Scripter-Steps'; package: 'Bloc-Scripter' : low-level keyboard events (key down, key up, key press) defined by subclasses

- BlDevScripterMouseStep BlDevScripterStep << #BlDevScripterMouseStep slots: { #modifiers }; tag: 'Scripter-Steps'; package: 'Bloc-Scripter' : low-level mouse events (mouse down, mouse up, mouse move) defined by subclasses

- BlDevScripterToolActionStep BlDevScripterMultipleActionStep << #BlDevScripterToolActionStep slots: { #target }; tag: 'Scripter-Steps'; package: 'Bloc-Scripter' : domain-specific composite steps for GT tools (coder, inspector, pager, filter, etc.) defined by subclasses

- BlDevScripterSetStep BlDevScripterTargetedStep << #BlDevScripterSetStep slots: { #storage }; tag: 'Scripter-Steps'; package: 'Bloc-Scripter' : stores values into the scripter's state during step execution (see BlScripterSetStepUsageExamples BlScripterStepUsageExamples << #BlScripterSetStepUsageExamples slots: {}; tag: 'Examples'; package: 'Bloc-Scripter' )

- BlDevScripterActionStep BlDevScripterTargetedStep << #BlDevScripterActionStep slots: { #block }; tag: 'Scripter-Steps'; package: 'Bloc-Scripter' : executes an arbitrary block during scripter playback (see BlScripterActionStepUsageExamples BlScripterStepUsageExamples << #BlScripterActionStepUsageExamples slots: {}; tag: 'Examples'; package: 'Bloc-Scripter' )

- BlDevScripterCheckStep BlDevScripterTargetedStep << #BlDevScripterCheckStep slots: {}; tag: 'Scripter-Steps'; package: 'Bloc-Scripter' : conditions about the current element state defined by subclasses

Steps should be used though an internal DSL not directly by instantiating step class.

The step-creation DSL is defined in the trait TBlDevScripterActionStep Trait << #TBlDevScripterActionStep tag: 'Scripter'; package: 'Bloc-Scripter' . This trait provides factory methods like #selector click "Simulates a click event on a target element" <scripterActionStep> ^ self addStep: (BlDevScripterClickStep new referenceSender) , #selector doubleClick "Simulates a double click event on a target element" <scripterActionStep> ^ self addStep: (BlDevScripterDoubleClickStep new referenceSender) , #selector type: aString ^ self type text: aString; referenceSender , #selector shortcut "Simulates a keyboard shortcut event on a target element. If no target element is provided, the shortcut is sent to the scripted element." <scripterActionStep> ^ self addStep: BlDevScripterShortcutStep new referenceSender , #selector keyPress: aBlKeyboardKey "Simulates a keyboard key click event sent to the focused element" ^ self keyPress key: aBlKeyboardKey; referenceSender , #selector mouseDown "Simulates a mouse down event at the current cursor position" <scripterActionStep> ^ self addStep: (BlDevScripterMouseDownStep new referenceSender) , #selector do "Performs a block closure with a target element as an argument" <scripterActionStep> ^ self addStep: BlDevScripterActionStep new referenceSender , #selector set "Assigns various optional variables in the scripter such as model or key:value pairs" <scripterActionStep> ^ self addStep: BlDevScripterSetStep new referenceSender , #selector wait ^ self addStep: BlDevScripterWaitStep new referenceSender , and more. Explore its methods to see all available step creators.

For assertions, the trait TBlDevScripterCheckStepCreation Trait << #TBlDevScripterCheckStepCreation tag: 'Scripter'; package: 'Bloc-Scripter' provides #selector check ^ (BlDevScripterFutureCheckStep new parent: self) referenceSender and #selector assert "Builds a step to assert the state of the graphical scene" <scripterActionStep> ^ (BlDevScripterFutureCheckStep new parent: self) referenceSender steps which return a BlDevScripterFutureCheckStep BlDevScripterCheckStep << #BlDevScripterFutureCheckStep traits: {TBlDevScripterCheckStep + TBlDevScripterCheckStepCreation + TBlDevScripterCompositeStepTarget}; slots: { #parent . #hasCustomTarget . #realStep }; tag: 'Scripter-Steps'; package: 'Bloc-Scripter' builder supporting: #selector satisfies: anOneArgBlock "Given block evaluated with the current target returns true." <scripterAssert> ^ self addStep: (BlDevScripterCheckElementStep new block: anOneArgBlock; referenceSender) , #selector value: anOneArgBlock equals: anEqualsBlock "Computed value equals to the provided one." <scripterAssert> ^ self addStep: (BlDevScripterCheckElementValueStep new valueBlock: anOneArgBlock; equalsBlock: anEqualsBlock; referenceSender) , #selector exists "Target element (or other specified object) exists." <scripterAssert> ^ self addStep: (BlDevScripterCheckElementExistsStep new referenceSender) , #selector notExists "Target element (or other specified object) doesn't exist." <scripterAssert> ^ self addStep: (BlDevScripterCheckElementNotExistsStep new referenceSender) , #selector childrenCount: aNumber ^ self addStep: (BlDevScripterCheckChildrenCountStep new childrenCount: aNumber; referenceSender) , #selector hasFocus ^ self addStep: (BlDevScripterCheckElementStep new label: 'Assert element has focus'; block: [ :anElement | anElement hasFocus ]; description: [ :anElement | '{1} element must have focus' format: {anElement} ]; referenceSender) , and more.

To discover available steps, browse its class hierarchy.

Key principles:

- Steps with the Step: suffix (e.g., #selector clickStep: aBlock | step | step := self click. step referenceSender. aBlock value: step. step play , #selector assertStep: aBlock | step | step := self assert. aBlock value: step. step play , #selector doStep: aBlock | step | step := self do. step referenceSender. aBlock value: step. step play ) that take a block to create the step auto-play immediately; no need to call BlScripter>>#play play rootStep play /BlDevScripterStep>>#play play self assert: [ self scripter notNil ] description: [ 'Can not play without scripter' ]. self playNoPulse. self privatePulseUntilReady .

clickOnButtonAndAssertWithImplicitPlay
	"Demonstrates clicking and then asserting the UI changed.
	Shows the pattern: action step followed by assertion step 
	with implicit play actions in the step creation method"
	<gtExample>
	| aScripter |
	aScripter := self scripterWithButton.
	
	aScripter clickStep: 
			[:s |
			s
				label: 'Click the test button';
				id: #testButton].
	aScripter assertStep: 
			[:s |
			s
				label: 'Assert button still exists';
				exists;
				id: #testButton].
				
	^aScripter
    

- Steps without the suffix (e.g., #selector click "Simulates a click event on a target element" <scripterActionStep> ^ self addStep: (BlDevScripterClickStep new referenceSender) , #selector assert "Builds a step to assert the state of the graphical scene" <scripterActionStep> ^ (BlDevScripterFutureCheckStep new parent: self) referenceSender ) require explicit play at the end (BlScripter>>#play play rootStep play /BlDevScripterStep>>#play play self assert: [ self scripter notNil ] description: [ 'Can not play without scripter' ]. self playNoPulse. self privatePulseUntilReady .). In case a step has the version that does the auto-play imediately favour that one.

clickOnButtonAndAssertWithExplicitPlay
	"Demonstrates clicking and then asserting the UI changed.
	Shows the pattern: action step followed by assertion step 
	with explicit play actions"
	<gtExample>
	| aScripter |
	aScripter := self scripterWithButton.
	
	aScripter click
			label: 'Click the test button';
			id: #testButton;
			play.
	aScripter assert
			label: 'Assert button still exists';
			exists;
			id: #testButton;
			play.
				
	^aScripter
    

- Use #selector substep: aLabel do: anOneArgBlock ^ self substeps: aLabel do: anOneArgBlock to group related steps logically with a label

mouseDownAndUp
	"Demonstrates low-level mouse events: mouseDown followed by mouseUp.
	These are the building blocks of click but give more control over timing and position."
	<gtExample>
	| aScripter container element |
	element := (BlElement new)
				id: #testElement;
				size: 100 @ 100;
				background: Color blue.
	container := (BrFrame new)
				matchParent;
				addChild: element.
	aScripter := self scripter.
	aScripter element: container.
	aScripter substep: 'Mouse down then up'
		do: 
			[:aStep |
			(aStep mouseDown)
				label: 'Press mouse button';
				play.
			(aStep mouseUp)
				label: 'Release mouse button';
				play].
	^aScripter
    

- The trait TBlDevScripterExamples Trait << #TBlDevScripterExamples tag: 'Scripter'; package: 'Bloc-Scripter' provides factory methods (#selector scripter <gtExample> <return: #BlScripter> | aScripter | aScripter := BlScripter new. self assert: aScripter events isEmpty. ^ aScripter , #selector scripterWithElement: anElementBlock <gtExample> ^ self scripter: self scripter withModel: [ nil ] element: anElementBlock , #selector scripterWithModel: aModelBlock element: anElementBlock <gtExample> ^ self scripter: self scripter withModel: aModelBlock element: anElementBlock ) — use it in example classes

Steps locate elements using a fluent DSL defined in the trait TBlDevScripterTarget Trait << #TBlDevScripterTarget tag: 'Scripter'; package: 'Bloc-Scripter' (mixed into BlDevScripterCompositeStep BlDevScripterStep << #BlDevScripterCompositeStep traits: {TBlDevScripterCompositeStepTarget + TBlDevScripterTarget}; slots: { #steps }; tag: 'Scripter-Steps'; package: 'Bloc-Scripter' and BlDevScripterTargetedStep BlDevScripterStep << #BlDevScripterTargetedStep traits: {TBlDevScripterTarget}; slots: { #target }; tag: 'Scripter-Steps'; package: 'Bloc-Scripter' ).

Examples of generic methods for controling the scripter target:

- #selector id: aBlElementId "Find an element with a given index (depth first)" <scripterStepTarget> self updateTarget: (BlDevScripterElementIdEventTarget new elementId: aBlElementId) — find by element ID - #selector // aBlElementQueryByPropertySelector "Selects among all children (depth first) based on a given selector such as id or class" <scripterStepTarget> self updateTarget: (BlDevScripterOneElementQueryEventTarget new // aBlElementQueryByPropertySelector) — descendant search (like CSS descendant selector) - #selector onChildAt: aNumber "Select the Nth direct child of the current target" <scripterStepTarget> self updateTarget: (BlDevScripterIndexedChildEventTarget new index: aNumber) — nth child (1-based) - #selector onBreadthFirstChildOfClass: aClass self updateTarget: (BlDevScripterDeepChildOfClassEventTarget new elementClass: aClass) — breadth-first search by class - #selector @ aBlElementQueryByPredicate "Filters the result of the previous selection based on a predicate (such as index)" <scripterStepTarget> self updateTarget: (BlDevScripterOneElementQueryEventTarget new @ aBlElementQueryByPredicate) — filter by block predicate - #selector onModel self target: (BlDevScripterModelTarget new) — target the scripter's model instead of an element - #selector onSelf self target: BlDevScripterSelfEventTarget new — target the current element - #selector onSpace self target: (BlDevScripterSpaceTarget new) / #selector onSpaceRoot self target: BlDevScripterSpaceRootTarget new — target the space

A part from generic targeting methods, tools can add using extension metods other methods to TBlDevScripterTarget Trait << #TBlDevScripterTarget tag: 'Scripter'; package: 'Bloc-Scripter' that provide domain-specific ways to identify elements, like TBlDevScripterTarget>>#onPagerPaneWithIndex: onPagerPaneWithIndex: anInteger GtPagerSettings isTreePager ifFalse: [ self // (GtPagerPageElementId indexed: anInteger) ] ifTrue: [ (self // GtTreePagerPaneElement) @ [ :eachPaneElement | eachPaneElement constraints horizontalTreeFlow depth = anInteger and: [ eachPaneElement constraints horizontalTreeFlow row = 1 ] ] ] providing a way to select within a navigation session the page at a given index.

inspectorInPagerClickInspectSpawnsPane
	"Demonstrates clicking the inspect button in an inspector within a pager.
	Verifies that a second pane is spawned with the same inspected object."
	<gtExample>
	| aScripter |
	aScripter := self scripter.
	aScripter element: (GtPagerSettings usedPager 
		createWrappedOn: (GtInspectorTool forObject: 42)).
		
	aScripter inspectorStep
		label: 'Click on the inspect button';
		clickOnInspectButton;
		play.
		
	aScripter inspectorStep
		label: 'Check the object in the second pane';
		onPagerPaneWithIndex: 2;
		assertInspectedObjectOfType: SmallInteger;
		play.
		
	^ aScripter
    

All these can be chained for nested navigation (illustrative):

"Chained targeting example:"
aScripter assertStep: [ :aStep |
	aStep 
		// GtPhlowColumnedListId;
		id: #scrollable;
		// GtPhlowCellElement;
		@ [ :aCellElement | aCellElement content text asString = 'target' ];
		satisfies: [ :el | el isNotNil ] ].
  

For testing GT tools, higher-level step APIs exist as subclasses of BlDevScripterToolActionStep BlDevScripterMultipleActionStep << #BlDevScripterToolActionStep slots: { #target }; tag: 'Scripter-Steps'; package: 'Bloc-Scripter' . These provide fluent DSLs tailored to their domain. To find them, browse the subclasses of BlDevScripterToolActionStep BlDevScripterMultipleActionStep << #BlDevScripterToolActionStep slots: { #target }; tag: 'Scripter-Steps'; package: 'Bloc-Scripter' .

Each tool step is accessed via a method on BlScripter Object << #BlScripter traits: {TBlDevScripterActionStep + TBlDevScripterCheckStepCreation}; slots: { #element . #space . #events . #rootStep . #eventHandler . #maxPulseElapsedTime }; tag: 'Scripter'; package: 'Bloc-Scripter' (e.g., #selector multipleViewsToolStep <scripterActionStep> ^ self addStep: (GtPhlowMultipleViewsToolStep new label: 'Multiple Views Tool'; referenceSender; onParentStepTarget: self) , #selector phlowCompositeToolStep <scripterActionStep> ^ self addStep: (GtPhlowCompositeToolStep new label: 'Phlow Composite Tool'; referenceSender; onParentStepTarget: self) , #selector inspectorStep <scripterActionStep> ^ self addStep: (GtInspectorStep new label: 'Inspector'; referenceSender; onParentStepTarget: self) , #selector methodsCoder <scripterActionStep> ^ self addStep: (GtPharoMethodsCoderStep new label: 'Pharo methods coder'; referenceSender) ). Look at senders of these methods for usage patterns.

Examples of tool step classes:

- GtPhlowViewsBasedToolStep BlDevScripterToolActionStep << #GtPhlowViewsBasedToolStep slots: {}; tag: 'Scripter'; package: 'GToolkit-Phlow' — assert tab names, click tabs, check tool objects - GtPhlowCompositeToolStep BlDevScripterToolActionStep << #GtPhlowCompositeToolStep slots: {}; tag: 'Scripter'; package: 'GToolkit-Phlow' — assert composite tool structure and labels - GtPagerStep BlDevScripterToolActionStep << #GtPagerStep slots: {}; tag: 'Scripter'; package: 'GToolkit-Pager' / GtTreePagerStep BlDevScripterToolActionStep << #GtTreePagerStep slots: {}; tag: 'Examples'; package: 'GToolkit-TreePager' — navigate pager panes - GtPharoMethodCoderStep BlDevScripterToolActionStep << #GtPharoMethodCoderStep slots: {}; tag: 'Scripter'; package: 'GToolkit-Pharo-Coder-Method-Examples' — expand/focus methods, browse implementors - BrEditorStep BlDevScripterToolActionStep << #BrEditorStep slots: {}; tag: 'Scripter'; package: 'Brick-Editor-Examples' — editor-specific assertions and interactions - GtFilterStep BlDevScripterToolActionStep << #GtFilterStep slots: {}; tag: 'Filters - Widgets'; package: 'GToolkit-Coder-UI' — filter-related steps

To find how a specific step is used in practice:

1. Browse subclasses of BlScripterStepUsageExamples Object << #BlScripterStepUsageExamples traits: {TBlDevScripterExamples}; slots: {}; tag: 'Examples'; package: 'Bloc-Scripter' — each demonstrates a specific step type with canonical patterns

2. Search for classes with "Scripter" and "Examples" in their name — there are many such classes across all layers

3. Look at senders of the step's factory method (e.g., senders of #selector clickStep: aBlock | step | step := self click. step referenceSender. aBlock value: step. step play , #selector shortcut "Simulates a keyboard shortcut event on a target element. If no target element is provided, the shortcut is sent to the scripted element." <scripterActionStep> ^ self addStep: BlDevScripterShortcutStep new referenceSender , #selector typeStep: aBlock | step | step := self type. step referenceSender. aBlock value: step. step play ) to see real-world usage

4. Use the trait TBlDevScripterExamples Trait << #TBlDevScripterExamples tag: 'Scripter'; package: 'Bloc-Scripter' as starting point — it provides the standard scripter factory

Key example classes by layer:

- Bloc (low-level): BlScripterExamples Object << #BlScripterExamples slots: { #elementExamples }; tag: 'Examples'; package: 'Bloc-Scripter' , BlElementBoundsByScripterExamples Object << #BlElementBoundsByScripterExamples slots: {}; tag: 'Basic'; package: 'Bloc-Examples' - Brick (widgets): BrSimpleListByScripterExamples Object << #BrSimpleListByScripterExamples traits: {TBlDevScripterExamples}; slots: {}; tag: 'List'; package: 'Brick-Examples' , BrTabGroupByScripterExamples Object << #BrTabGroupByScripterExamples traits: {TBlDevScripterExamples}; slots: {}; tag: 'Tab'; package: 'Brick-Examples' , BrMenuWithScripterExamples Object << #BrMenuWithScripterExamples traits: {TBlDevScripterExamples}; slots: {}; tag: 'Menu'; package: 'Brick-Examples' , BrEditorWithScripterExamples Object << #BrEditorWithScripterExamples slots: {}; tag: 'Editor'; package: 'Brick-Examples' - GToolkit (tools): GtBehaviorCoderByScripterExamples Object << #GtBehaviorCoderByScripterExamples traits: {TCoderByScripterExamples}; slots: { #environment }; tag: 'Coders'; package: 'GToolkit-Pharo-Coder-Examples' , GtWorldByScripterExamples Object << #GtWorldByScripterExamples traits: {TBlDevScripterExamples + TGtScripterDebuggerExamples}; slots: {}; tag: 'Examples'; package: 'GToolkit-World' , GtInspectorScripterExamples Object << #GtInspectorScripterExamples slots: {}; tag: 'Examples'; package: 'GToolkit-Demo-MoldableDevelopment' , GtSpotterByScripterExamples Object << #GtSpotterByScripterExamples slots: {}; tag: 'Examples'; package: 'GToolkit-Spotter-Examples' - Lepiter: LeDatabaseByScripterExamples Object << #LeDatabaseByScripterExamples traits: {TBlDevScripterExamples}; slots: {}; tag: 'Examples'; package: 'Lepiter-UI' , LePharoSnippetByScripterExamples Object << #LePharoSnippetByScripterExamples traits: {TBlDevScripterExamples}; slots: {}; tag: 'Examples'; package: 'Lepiter-Pharo'

Use labelslabel: 'descriptive text' on every step aids debugging when a step fails.

Use substep:do: for grouping related steps logically.

Target precisely — identify the element on a which a step should be appplied

Check TBlDevScripterActionStepTrait << #TBlDevScripterActionStep tag: 'Scripter'; package: 'Bloc-Scripter' methods for all available actions before inventing custom approaches.

Browse BlScripterStepUsageExamplesObject << #BlScripterStepUsageExamples traits: {TBlDevScripterExamples}; slots: {}; tag: 'Examples'; package: 'Bloc-Scripter' subclasses for canonical usage patterns of each step type.

Use the trait TBlDevScripterExamples Trait << #TBlDevScripterExamples tag: 'Scripter'; package: 'Bloc-Scripter' in your example classes for standard scripter factories.

Steps auto-pulse — no need to manually pulse the space; the scripter does this after each step; unless explicitly needed also no need to manually wait for steps to complete.

Compose examples — build on prior examples via self priorExample to get a scripter in a known state, then add more steps.