objs-core 1.1.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/EXAMPLES.md +1637 -0
- package/README.md +346 -75
- package/SKILL.md +500 -0
- package/objs.built.js +2657 -0
- package/objs.built.min.js +67 -0
- package/objs.d.ts +455 -0
- package/objs.js +3805 -0
- package/package.json +70 -37
- package/objs.1.1.js +0 -1205
- package/objs.1.1.js.zip +0 -0
- package/objs.1.1.min.js +0 -2
- package/objs.1.1.min.js.zip +0 -0
- package/objs.npm.1.1.js +0 -16
package/README.md
CHANGED
|
@@ -1,19 +1,82 @@
|
|
|
1
1
|
# Objs
|
|
2
|
-
> Fast and simple library to speed up developing by
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
> Fast and simple library to speed up developing by AI context friendly architecture, auto-tests recording, cache control and other. Develop new features without rewriting anything. Works standalone or alongside React. Examples and full documentation: [Full Documentation](https://en.fous.name/objs/documentation)
|
|
3
|
+
|
|
4
|
+
**AI-friendly** — one file, `SKILL.md` primer (~6,000 tokens). An LLM generates correct Objs code from a description without JSX, virtual DOM, or React lifecycle knowledge.
|
|
5
|
+
|
|
6
|
+
**React-developer-friendly** — familiar `className`, `ref`/`refs`, `o.createStore`. Add one script tag to an existing React app and get Playwright test generation without touching any components.
|
|
7
|
+
|
|
8
|
+
**Live examples** — real patterns in [`examples/`](https://foggysq.github.io/objs/examples/), narrative walkthroughs in [`EXAMPLES.md`](EXAMPLES.md).
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Why Objs
|
|
13
|
+
|
|
14
|
+
**Core functionality** — Record user actions in the browser, export ready-to-commit tests, and run them in CI. One script; no separate recorder or test-ID maintenance.
|
|
15
|
+
|
|
16
|
+
- **Record → Playwright in one pipeline** — `o.startRecording()` captures click, input, change, scroll; `o.stopRecording()` returns actions and auto-generated assertions; `o.exportPlaywrightTest(recording)` outputs a `.spec.ts` with locators and network mocks. Paste into your repo and run `npx playwright test`.
|
|
17
|
+
- **CI support** — Export runs in all builds (including prod). QA or assessors record on staging; paste the generated Playwright test into your test suite; CI runs it with no extra config. Optional `o.exportTest(recording)` for Objs-style tests.
|
|
18
|
+
- **Store and update model** — `o.createStore()` + `subscribe`/`notify`: no virtual DOM, no re-render cascade; only subscribed components update their own DOM (O(1) per subscriber).
|
|
19
|
+
- **One library, many roles** — DOM + state + routing + AJAX + cache (`o.inc`) + tests + recording + SSR. No extra test runner or recorder product. Built-in `o.route()` / `o.router()` — no separate router dependency.
|
|
20
|
+
- **Stable selectors and UI checks** — `{...o.reactQA('ComponentName')}` or `o.autotag`; recorder uses `data-qa` and list indices. `o.assertSize(el, { w, h, padding, margin })` for design system verification; `o.testConfirm()` for manual/hover checks after replay.
|
|
21
|
+
- **Works standalone or with React** — Add one script tag to an existing React app; no architecture change. Familiar `className`, `ref`/`refs`, `o.createStore`. Built-in SSR (Node) with `DocumentMVP` and in-browser hydration.
|
|
22
|
+
- **AI-friendly** — One file, ~6,000-token `SKILL.md` primer. No JSX, virtual DOM, or React lifecycle; fewer tokens than typical React context for runnable output. No stale closures, dependency arrays, or re-render cascades. Same code runs in Node (SSR) so tools can verify output without a browser — **verify generated code without user review**: run `o.init(states).render()` in Node, serialize or assert structure before returning to the user.
|
|
23
|
+
|
|
24
|
+
→ [Full comparison and live demo](https://foggysq.github.io/objs/examples/ai-workflow/index.html)
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
### Update v2.0: New features for micro-service architecture and AI development
|
|
29
|
+
|
|
30
|
+
#### Breaking changes (migrate first)
|
|
31
|
+
- **Build output** — Distribution files are now `objs.built.js` and `objs.built.min.js` (from `node build.js`). `objs.prod.js` / `objs.dev.js` are no longer produced; use the built files or `objs.js` for development.
|
|
32
|
+
- **`attr(name, value)`** — `null` removes the attribute; `""` sets it to empty string. Previously `""` could remove; now use `attr(name, null)` to remove.
|
|
33
|
+
- **`style(value)`** — `null` removes the `style` attribute entirely. Use `style(null)` or `css(null)` to clear; empty string no longer removes.
|
|
34
|
+
- **`css(object)`** — `null` removes the `style` attribute entirely. Use `css(null)` to clear; `css({})` or `style('')` no longer remove.
|
|
35
|
+
- **`addClass(...cls)`** / **`removeClass(...cls)`** — Now accept multiple arguments: `addClass('a', 'b', 'c')`. Single-string usage remains valid.
|
|
36
|
+
|
|
37
|
+
#### New
|
|
38
|
+
- **`o.startRecording()`** — QA testers can start actions recording on staging
|
|
39
|
+
- **`o.stopRecording()`** — Stop recording
|
|
40
|
+
- **`o.exportTest()`** — Export tests code as ready to review and use
|
|
41
|
+
- **Build** — `objs.js` is the source (development); `node build.js` produces `objs.built.js` and `objs.built.min.js` (ESM + window.o) for distribution.
|
|
42
|
+
- **`o.assertSize(el, expected)`** — Can compare padding and margin (design system / UI verification): `padding`, `paddingTop`/`Right`/`Bottom`/`Left`, `margin`, `marginTop`/`Right`/`Bottom`/`Left` in addition to `w` and `h`
|
|
43
|
+
- **`val([value])`** — Get/set `.value` on `input`/`textarea`/`select`
|
|
44
|
+
- **`refs` on ObjsInstance** — Auto-collects `ref="name"` child elements as ObjsInstances on `init`
|
|
45
|
+
- **`className` in render** — Alias for `class` in render descriptors (React familiarity)
|
|
46
|
+
- **`o.createStore(obj)`** — Reactive store with `subscribe` / `notify` / `reset`
|
|
47
|
+
- **`o.reactQA(name)`** — Returns `{ 'data-qa': 'kebab-name' }` for React JSX spread
|
|
48
|
+
- **`o.exportPlaywrightTest(recording, options?)`** — Generates a ready-to-run Playwright `.spec.ts` from a recording
|
|
49
|
+
- **Tests auto-generation from user recordings** — record interactions, export as committed test code
|
|
50
|
+
- **Redux / MobX / React Context adapters** with granular-update listener pattern
|
|
51
|
+
- **TypeScript definitions** (`objs.d.ts`) covering the full public API
|
|
52
|
+
- **QA autotag** (`o.autotag`) — auto-sets `data-qa` from component name
|
|
53
|
+
- **Test overlay** — fixed UI panel with results and JSON export
|
|
54
|
+
- Data loaders/stores and connection to component state updates
|
|
55
|
+
- SSR (Server side render) and in-browser hydration
|
|
56
|
+
- Tests with page reload, Cookies and SS/LS deletion for e2e
|
|
57
|
+
|
|
58
|
+
#### Fixes
|
|
59
|
+
- Object.assign() for state props to save input consistency
|
|
60
|
+
- `attr(name, null)` now correctly removes attributes; `attr(name, '')` sets empty string
|
|
61
|
+
- if test() gets a verification title without test function, it logs it as a text divider
|
|
62
|
+
- `.html('')` sets innerHTML to `''`
|
|
63
|
+
- append attribute in state adds child nodes, childNodes/children — replace and add children
|
|
64
|
+
- `.add(element)` works only for got elements (not inited)
|
|
65
|
+
- all Objs Cookies/LS/SS names start with `'oTest-'` or `'oInc-'`
|
|
66
|
+
- use `o().store = {}` to save component data instead of root properties
|
|
67
|
+
- `console.error()` for error output by default
|
|
68
|
+
- `o.inc()` can cache files from urls starting with protocol, if `o.incCors` is false (default)
|
|
69
|
+
- `o.first()` gets one element and runs `select(0)` automatically
|
|
70
|
+
- added parent check for `remove()` method
|
|
8
71
|
|
|
9
72
|
|
|
10
73
|
## Get started
|
|
11
|
-
Just
|
|
74
|
+
Just import in your project or include script on the page.
|
|
12
75
|
```
|
|
13
|
-
|
|
76
|
+
npm i objs-core
|
|
14
77
|
```
|
|
15
78
|
```
|
|
16
|
-
|
|
79
|
+
<script src="objs.built.min.js" type="text/javascript"></script>
|
|
17
80
|
```
|
|
18
81
|
|
|
19
82
|
|
|
@@ -23,18 +86,18 @@ npm i objs-core
|
|
|
23
86
|
|
|
24
87
|
#### Develop
|
|
25
88
|
- Samples and state control
|
|
26
|
-
-
|
|
27
|
-
-
|
|
89
|
+
- Data store, Cookies and LS/SS control
|
|
90
|
+
- Events delegation
|
|
28
91
|
|
|
29
92
|
#### Test
|
|
30
|
-
- Sync/async tests
|
|
93
|
+
- Sync/async, tests with reload
|
|
31
94
|
- Console & HTML output
|
|
32
95
|
- Autotests
|
|
33
96
|
|
|
34
97
|
#### Optimize
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
98
|
+
- Separate logic and samples
|
|
99
|
+
- Native micro-service architecture
|
|
100
|
+
- Async loading and preloading, cache
|
|
38
101
|
|
|
39
102
|
|
|
40
103
|
|
|
@@ -111,38 +174,145 @@ timer.stop();
|
|
|
111
174
|
`o.errors` – an array of all hidden errors, can be logged by `o.logErrors()` for debug
|
|
112
175
|
`o.onError` – a function than will be called with an error as an argument
|
|
113
176
|
|
|
114
|
-
> This and some more complex live examples are in the [full documentation](https://fous.name/objs). There are lots of useful methods and settings.
|
|
177
|
+
> This and some more complex live examples are in the [full documentation](https://fous.name/objs/documentation). There are lots of useful methods and settings.
|
|
178
|
+
|
|
115
179
|
|
|
180
|
+
### Tests - unit tests, e2e, recording etc.
|
|
181
|
+
|
|
182
|
+
Testing is a first-class part of Objs: use `o.test()` and `o.addTest()` for sync and async unit tests, including tests with page reload and autorun. Record user sessions with `o.startRecording()` / `o.stopRecording()`, then export to Objs-style tests (`o.exportTest()`) or Playwright (`.spec.ts`) with `o.exportPlaywrightTest()` for e2e in CI. Replay with `o.playRecording()` (all builds); call `o.testOverlay()` to show a results panel so assessors can see if all auto tests passed and which manual checks failed. Use `o.testConfirm()` for manual checks (e.g. hover effects). Dev-only: `o.assertSize()` / `o.assertVisible()` for layout assertions. See the [recording example](https://foggysq.github.io/objs/examples/recording/index.html) and the full documentation for details.
|
|
116
183
|
|
|
117
184
|
|
|
118
185
|
|
|
119
186
|
|
|
120
187
|
## Functions
|
|
121
|
-
Almost all functions return control object with methods, let's call it **Objs**.
|
|
188
|
+
Almost all functions return control object with methods, let's call it **Objs**. Full API and TypeScript types: [objs.d.ts](objs.d.ts).
|
|
122
189
|
|
|
123
|
-
###
|
|
190
|
+
### Element selection
|
|
124
191
|
`o(q)` – gets elements to control object. If [string] - by **querySelectorAll(q)** into control object, if DOM element or an array of them - gets them, if [number] - gets control object from **o.inits[q]**.
|
|
125
192
|
|
|
126
193
|
`o.first(q)` – gets element to control by **querySelector(q)**.
|
|
127
194
|
|
|
128
195
|
`o.take(q)` – gets elements like **o(q)** from DOM but if there is just one element or equal number of elements to inited in **o.inits[]** before, gets all inited elements and their methods.
|
|
129
196
|
|
|
130
|
-
|
|
197
|
+
### Component control
|
|
131
198
|
`o.init(states)` – returns **Objs**, creates method(s) for each state to create, change elements. State called **render** is reserved for creation elements. **states** can be [string], [object], [function] that returns [string] or [object]. After **init()** **Objs** gets a **initID** parameter for a saved object in **o.inits**. More info about structure and features [here](https://fous.name/objs).
|
|
132
199
|
|
|
133
200
|
`o.initState(state, [props])` – inite method and call it with props, e.g. to render/create element. **Objs** gets a **.initID** parameter for a saved object in **o.inits[]**.
|
|
134
201
|
|
|
135
202
|
`o.inits[initID]` – an array of all inited objects. Available by index **initID** or **o.take()**.
|
|
136
203
|
|
|
137
|
-
`o.
|
|
204
|
+
Instance: `o().init()` – equal to **o.init()** but with elements to control. `o().initState()` – equal to **o.initState()** but with elements to control. `o().sample()` – returns states object with render state for creation such elements. `o().getSSR(initId)` – hydrate from existing DOM. `o().saveState([id])`, `o().revertState([id])`, `o().loseState(id)` – save/restore DOM state. `o().unmount()` – remove from DOM and **o.inits**. `o().connect(loader, state, fail)` – connect a loader to this instance (state/fail method names). `o().initID` – undefined or number in **o.inits[]**. `o().html([html])` – returns html string of all elements or sets innerHTML as **html**.
|
|
205
|
+
|
|
206
|
+
### DOM manipulation
|
|
207
|
+
`o().reset(q)` – clears **Objs** and get new elements by **q**, works as **o()**.
|
|
208
|
+
|
|
209
|
+
`o().select([i])` – selects number **i** element from 0 to change only it, if **i** is undefined selects the last index element.
|
|
210
|
+
|
|
211
|
+
`o().all()` – selects all elements to operate again.
|
|
212
|
+
|
|
213
|
+
`o().remove([i])` – removes all or **i** element from DOM.
|
|
214
|
+
|
|
215
|
+
`o().skip(i)` – removes **i** element from control set of this **Objs**.
|
|
216
|
+
|
|
217
|
+
`o().add()` – adds element to control set.
|
|
218
|
+
|
|
219
|
+
`o().find(q)` – finds all children elements by q-query in each element.
|
|
220
|
+
|
|
221
|
+
`o().first(q)` – finds only the first child element by q-query in each element.
|
|
222
|
+
|
|
223
|
+
`o().length` – number of elements of control set.
|
|
224
|
+
|
|
225
|
+
`o().el` – the first DOM element in the set.
|
|
226
|
+
|
|
227
|
+
`o().els` – all DOM elements of the set.
|
|
228
|
+
|
|
229
|
+
`o().last` – the last DOM element in the set.
|
|
230
|
+
|
|
231
|
+
`o().attr(attribute, [value])` – `UPDATED` sets **attribute** to **value**. Pass `null` to remove the attribute. Pass `""` to set an empty string. Returns **attribute** value if **value** is undefined. If **.select()** was not used before — returns an array of values.
|
|
232
|
+
|
|
233
|
+
`o().attrs()` – returns an array of all elements attributes, if **.select()** was used before - returns an object with values of one element.
|
|
234
|
+
|
|
235
|
+
`o().dataset([object])` – Sets dataset values due to the **object** data. It will not delete other dataset values. If **.select()** was used before - returns an object with dataset of one element or changes just one element.
|
|
236
|
+
|
|
237
|
+
`o().style(value)` – `UPDATED` sets style attribute to [string] **value**. Pass `null` to remove the `style` attribute entirely.
|
|
238
|
+
|
|
239
|
+
`o().css(object|null)` – `UPDATED` sets style from **object** like `{width: '100px', 'font-family': 'Arial'}`. Pass `null` to remove the `style` attribute entirely.
|
|
240
|
+
|
|
241
|
+
`o().val([value])` – `NEW` gets or sets the `.value` property of `input`/`textarea`/`select`. Returns current value when called without argument; sets and returns `Objs` for chaining when called with argument.
|
|
242
|
+
|
|
243
|
+
`o().setClass(value)` – sets class attribute to **value**.
|
|
244
|
+
|
|
245
|
+
`o().addClass(...cls)` – `UPDATED` adds one or more classes: `addClass('foo', 'bar', 'baz')`.
|
|
246
|
+
|
|
247
|
+
`o().removeClass(...cls)` – `UPDATED` removes one or more classes: `removeClass('foo', 'bar')`.
|
|
248
|
+
|
|
249
|
+
`o().toggleClass(class, rule)` – switch having and not having **class** by **rule**. If **rule** set **class**.
|
|
250
|
+
|
|
251
|
+
`o().haveClass(class)` – returns true if all elements have **class**.
|
|
252
|
+
|
|
253
|
+
`o().innerHTML([html])` – if **html** is set, sets innerHTML of all elements. If not set, returns array with innerHTML of each element.
|
|
254
|
+
|
|
255
|
+
`o().innerText(text)` – sets innerText for all elements.
|
|
256
|
+
|
|
257
|
+
`o().textContent(content)` – sets textContent for all elements.
|
|
258
|
+
|
|
259
|
+
`o().refs` – `NEW` object populated on `init` — every child element with a `ref="name"` attribute is available as `component.refs.name` (an ObjsInstance wrapper). Use for direct access without selectors.
|
|
138
260
|
|
|
139
|
-
`o.
|
|
261
|
+
`o().forEach(function)` – runs **function** with an object as the first parameter: {o, self, i, el} where is o-function, self Objs object, i-index of current element and el - DOM element.
|
|
262
|
+
|
|
263
|
+
### Events
|
|
264
|
+
`o().on(events, function, [options])` – adds **events** listeners separated by ', ' to elements.
|
|
265
|
+
|
|
266
|
+
`o().off(events, function, [options])` – removes **events** listeners separated by ', ' to elements.
|
|
267
|
+
|
|
268
|
+
`o().offAll([event])` – removes all listeners or for special **event** from elements.
|
|
269
|
+
|
|
270
|
+
`o().onAll([event])` – adds all inited listeners from cache for all or for special **event**.
|
|
140
271
|
|
|
141
|
-
`o
|
|
272
|
+
`o().ie` – object with all ever added listeners like {click: [[function, options], ...], ...}.
|
|
273
|
+
|
|
274
|
+
### DOM insert
|
|
275
|
+
`o().appendInside(q)` – append elements inside element **q** or got by **q** query.
|
|
276
|
+
|
|
277
|
+
`o().appendBefore(q)` – append elements before element **q** or got by **q** query.
|
|
278
|
+
|
|
279
|
+
`o().appendAfter(q)` – append elements after element **q** or got by **q** query.
|
|
280
|
+
|
|
281
|
+
`o().prepareFor(React.createElement, [React.Component])` – clones and returns React element or JSX Component if React.Component is given. Allows to use Objs in React Apps. Objs states should be inited on rendered elements.
|
|
142
282
|
|
|
143
|
-
|
|
283
|
+
### State and store
|
|
284
|
+
`o.createStore(defaults)` – `NEW` creates a reactive plain-object store. Returns the defaults object extended with `subscribe(component, stateName)`, `notify()`, and `reset()`. Subscribed components receive `{ ...storeProps, self, o, i }` merged into their state context on every `notify()`.
|
|
144
285
|
|
|
145
|
-
|
|
286
|
+
```
|
|
287
|
+
Objs update cycle (vs React):
|
|
288
|
+
|
|
289
|
+
React: setState(newVal)
|
|
290
|
+
→ component function re-runs entirely
|
|
291
|
+
→ virtual DOM diff
|
|
292
|
+
→ patch (1–N nodes, including unchanged ones)
|
|
293
|
+
|
|
294
|
+
Objs: store.notify()
|
|
295
|
+
→ each subscribed component's sync() fires
|
|
296
|
+
→ each sync() writes only its own DOM nodes
|
|
297
|
+
→ O(1) per subscriber — no diff, no cascade
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
`o.connectRedux(store, selector, component, [state])` – connects a Redux store slice to a component state method. Fires immediately and on every store change. Returns unsubscribe function.
|
|
301
|
+
|
|
302
|
+
`o.connectMobX(mobx, observable, accessor, component, [state])` – wraps `mobx.autorun()` to connect a MobX observable to a component state method. Returns disposer.
|
|
303
|
+
|
|
304
|
+
`o.withReactContext(React, Context, selector, component, [state])` – returns a React bridge component that calls `component[state](selector(contextValue))` on every context change. Mount it inside the Provider to connect.
|
|
305
|
+
|
|
306
|
+
`o.ObjsContext` – default context value placeholder for `React.createContext()`.
|
|
307
|
+
|
|
308
|
+
### Routing
|
|
309
|
+
`o.route(path, task)` – register a route: **path** is string, boolean, or function(path); **task** is function or object. Returns match result. Built-in; no separate router dependency.
|
|
310
|
+
|
|
311
|
+
`o.router(routes)` – run routing: **routes** is object of path → task. Returns true if a route matched.
|
|
312
|
+
|
|
313
|
+
Use **o.getParams([key])** to read GET (query) parameters in route callbacks or when initialising components—e.g. pass `o.getParams()` to `render(data)` or read `o.getParams('id')` for component state or data loading.
|
|
314
|
+
|
|
315
|
+
### HTTP and parameters
|
|
146
316
|
`o.get(url, [props])` – returns promise for GET AJAX, **data** in **props** as an [object] will be converted to string parameters.
|
|
147
317
|
|
|
148
318
|
`o.post(url, props)` – returns promise for POST AJAX, **data** in **props** as an [object] will be converted to body.
|
|
@@ -151,13 +321,15 @@ Almost all functions return control object with methods, let's call it **Objs**.
|
|
|
151
321
|
|
|
152
322
|
`o.getParams([key])` – returns GET **key** value or an object with all GET parameters.
|
|
153
323
|
|
|
154
|
-
|
|
324
|
+
### Include and cache
|
|
155
325
|
`o.inc(sources, [callBack, callBad])` – returns [number] **setID**, gets **souces** is an object like {nameID: url, ...} where **nameID** is unique ID, **url** link to JS, CSS or image, **callBack** – function to run after everything is loaded successfully, **callBad** - function to run on failure. Functions gets **setN** as the first argument.
|
|
156
326
|
|
|
157
327
|
`o.incCheck(setID)` – true if include files set number **setID** is loaded.
|
|
158
328
|
|
|
159
329
|
`o.incCacheClear([all])` – true. Clears localStorage JS, CSS cache. If **all** is true, removes DOM elements of include and clears all include data.
|
|
160
330
|
|
|
331
|
+
`o.newLoader(promise)` – create a loader for async data; use with `o().connect(loader, state, fail)`.
|
|
332
|
+
|
|
161
333
|
`o.incCache` – true, cache in localStorage enabled.
|
|
162
334
|
|
|
163
335
|
`o.incCacheExp` – 1000 * 60 * 60 * 24, cache for 24 hours.
|
|
@@ -174,16 +346,35 @@ Almost all functions return control object with methods, let's call it **Objs**.
|
|
|
174
346
|
|
|
175
347
|
`o.incFns` – object, array of name:status for all loaded functions.
|
|
176
348
|
|
|
177
|
-
|
|
349
|
+
### Cookies and storage
|
|
350
|
+
`o.setCookie(name, value, [options])` – set a cookie.
|
|
351
|
+
|
|
352
|
+
`o.getCookie(name)` – get cookie value.
|
|
353
|
+
|
|
354
|
+
`o.deleteCookie(name)` – delete a cookie.
|
|
355
|
+
|
|
356
|
+
`o.clearCookies()` – clear all cookies.
|
|
357
|
+
|
|
358
|
+
`o.clearLocalStorage()`, `o.clearSessionStorage()`, `o.clearTestsStorage()` – clear respective storage.
|
|
359
|
+
|
|
360
|
+
`o.clearAfterTests()` – clear cookies and test-related storage after test run (e.g. in tAfterEach).
|
|
361
|
+
|
|
362
|
+
### Testing
|
|
178
363
|
`o.test(title, test1, test2, ..., callBack)` – returns [number] **testID**, gets [string] **title** and tests like ["Test title", testFunction], where **testFunction** should return true for success and false or string for failure. If test is async, **testFunction** should get the first parameter and use it in **o.testUpdate()**.
|
|
179
364
|
|
|
365
|
+
`o.addTest(title, ...cases)` – add a test suite; returns handle for **o.runTest()**.
|
|
366
|
+
|
|
367
|
+
`o.runTest(testId?, autoRun?, savePrev?)` – run test(s). **savePrev** true keeps existing sessionStorage for that testId so the run can resume.
|
|
368
|
+
|
|
180
369
|
`o.testUpdate(info, result, [description])` – returns undefined, gets **info** object (the first parameter of any **testFunction**) to update test status and set it to **result** (true or false/string), **description** - additional text if needed. Used for test status update for async tests. More info [here](https://fous.name/objs).
|
|
181
370
|
|
|
371
|
+
`o.updateLogs()` – return test log lines (e.g. for assertions).
|
|
372
|
+
|
|
182
373
|
`o.tLog[testID]` – test sessions and text results.
|
|
183
374
|
|
|
184
375
|
`o.tRes[testID]` – test sets results as true/false.
|
|
185
376
|
|
|
186
|
-
`o.tStatus[testID: [functionID: true/false],...]` – an array of set test functions statuses.
|
|
377
|
+
`o.tStatus[testID: [functionID: true/false],...]` – an array of set test functions statuses.
|
|
187
378
|
|
|
188
379
|
`o.tShowOk` – false, success tests are hidden, only errors. Set to **true** to see success results before **o.test()**.
|
|
189
380
|
|
|
@@ -191,94 +382,174 @@ Almost all functions return control object with methods, let's call it **Objs**.
|
|
|
191
382
|
|
|
192
383
|
`o.tTime` – 2000, milliseconds timeout for async tests.
|
|
193
384
|
|
|
194
|
-
|
|
195
|
-
Here are methods, **o()** means that they are available after getting elements from DOM or after init and render functions (after creating elements).
|
|
385
|
+
`o.tBeforeEach` / `o.tAfterEach` – global hooks called before/after each test case. Set to a function.
|
|
196
386
|
|
|
197
|
-
|
|
198
|
-
|
|
387
|
+
### Recording and export
|
|
388
|
+
Available in all builds so QA testers/assessors can record on staging or production environments.
|
|
199
389
|
|
|
200
|
-
`o
|
|
390
|
+
> **Security note:** `o.startRecording()` intercepts `window.fetch` and captures request/response bodies including auth tokens. Appropriate for staging environments; review before enabling on production.
|
|
201
391
|
|
|
202
|
-
`o().
|
|
392
|
+
`o.startRecording(observe?, events?, timeouts?)` – `UPDATED` starts capturing user interactions and network requests as mocks. Optional `observe` is a CSS selector to scope the MutationObserver (e.g. `'#task-app'`). Defaults: events `['click','mouseover','scroll','input','change']`, timeouts `{click:100, mouseover:50}`. Check **o.recorder.active** to see if recording is on.
|
|
203
393
|
|
|
204
|
-
`o
|
|
394
|
+
`o.stopRecording()` – `UPDATED` stops recording, returns `{actions, mocks, initialData, assertions, observeRoot, stepDelays}`. When scoped recording was used, `assertions` is an array of `{actionIdx, type, selector, text?|className?}` (from the MutationObserver), and `observeRoot` is the selector string or null. `stepDelays` is the per-event delay map (from `timeouts`) used when replaying.
|
|
205
395
|
|
|
206
|
-
`o
|
|
396
|
+
`o.exportTest(recording)` – `UPDATED` returns generated `o.addTest()` source code string ready to review and commit.
|
|
207
397
|
|
|
208
|
-
`o
|
|
398
|
+
`o.exportPlaywrightTest(recording, [options])` – `NEW` returns a complete Playwright `.spec.ts` file string with network mocks, `page.goto()`, typed locator steps, and TODO assertion comments. `options.testName` and `options.baseUrl` are optional.
|
|
209
399
|
|
|
210
|
-
|
|
400
|
+
```js
|
|
401
|
+
o.startRecording();
|
|
402
|
+
// QA tester uses the app normally
|
|
403
|
+
const rec = o.stopRecording();
|
|
404
|
+
console.log(o.exportPlaywrightTest(rec, { testName: 'Checkout flow' }));
|
|
405
|
+
// paste → tests/checkout.spec.ts → npx playwright test
|
|
406
|
+
```
|
|
211
407
|
|
|
212
|
-
`o
|
|
408
|
+
`o.clearRecording([id])` – removes recording from sessionStorage.
|
|
213
409
|
|
|
214
|
-
`o()
|
|
410
|
+
`o.playRecording(recording, [mockOverrides])` – Replays recording as a test with intercepted fetch. Available in all builds (for assessors on staging).
|
|
215
411
|
|
|
216
|
-
|
|
412
|
+
### QA and selectors
|
|
413
|
+
`o.autotag` – set to a string (e.g. `"qa"`) to auto-add `data-{autotag}="component-name"` to all rendered elements. Component name comes from `states.name` (camelCase → kebab-case). Ships in all builds — QA teams can target stable selectors with Playwright/Cypress.
|
|
217
414
|
|
|
218
|
-
`o()
|
|
415
|
+
`o.reactQA(componentName)` – `NEW` returns a `{ 'data-qa': 'kebab-name' }` object for spreading onto React JSX elements. Converts CamelCase to kebab-case. Respects `o.autotag` value. Ships in all builds.
|
|
219
416
|
|
|
220
|
-
|
|
417
|
+
```jsx
|
|
418
|
+
<button {...o.reactQA('CheckoutButton')} onClick={fn}>Checkout</button>
|
|
419
|
+
// → <button data-qa="checkout-button">
|
|
420
|
+
```
|
|
221
421
|
|
|
222
|
-
|
|
223
|
-
|
|
422
|
+
### Measurement and UI assertions (dev)
|
|
423
|
+
All builds include the full API (test framework, playRecording, testOverlay, testConfirm, measure/assertVisible/assertSize). Only the debug flag and debug logging are behind `__DEV__`.
|
|
224
424
|
|
|
225
|
-
`o
|
|
425
|
+
`o.measure(el)` – returns `{width, height, top, left, visible, opacity, zIndex}`. Use in test assertions.
|
|
226
426
|
|
|
227
|
-
`o
|
|
427
|
+
`o.assertVisible(el)` – returns `true/false` for use inside `o.test()`.
|
|
228
428
|
|
|
229
|
-
`o
|
|
429
|
+
`o.assertSize(el, expected)` – returns `true` or a descriptive error string. `expected` can include:
|
|
430
|
+
- `w`, `h` – width and height (px)
|
|
431
|
+
- `padding` – same value for all four sides (px), or `paddingTop`, `paddingRight`, `paddingBottom`, `paddingLeft` individually
|
|
432
|
+
- `margin` – same value for all four sides (px), or `marginTop`, `marginRight`, `marginBottom`, `marginLeft` individually
|
|
230
433
|
|
|
231
|
-
|
|
434
|
+
Use for design system or UI verification tests (e.g. button height 24px, container padding 20px).
|
|
232
435
|
|
|
233
|
-
|
|
234
|
-
`o().attr(attribute, [value])` – sets **attribute** to **value** or removes attribute if **value** is equal to '' or returns **attribute** value if **value** is undefined. If **.select()** was not used before - returns an array of values.
|
|
436
|
+
`o.testOverlay()` – Renders a fixed overlay button (🧪 Tests). Click to see pass/fail results for all test runs (auto steps and manual checks). For assessors: after replay, open the overlay to see if all auto tests passed and which manual checks failed. Available in all builds.
|
|
235
437
|
|
|
236
|
-
`o
|
|
438
|
+
`o.testConfirm(label, items?, opts?)` – Shows a draggable overlay titled "Label: Paused" with an optional checklist; returns `Promise<{ ok: boolean, errors?: string[] }>`. Use after replay for manual checks (e.g. hover effects). Available in all builds. See the [recording example](https://foggysq.github.io/objs/examples/recording/index.html) for a live demo.
|
|
237
439
|
|
|
238
|
-
|
|
440
|
+
### SSR and Node
|
|
441
|
+
In Node, **o.D** is **o.DocumentMVP** (no real DOM); **o.init().render()** builds a virtual tree and you can serialize with the same code path that produces HTML for SSR. See full docs for getSSR and hydration.
|
|
239
442
|
|
|
240
|
-
|
|
443
|
+
### Utilities and debug
|
|
444
|
+
**o.verify / o.safeVerify / o.specialTypes** — Runtime type checking for arguments, config, or API responses. Useful in projects to fail fast at API boundaries, validate options before use, or keep generated code safe.
|
|
241
445
|
|
|
242
|
-
|
|
446
|
+
- **o.verify(pairs, safe?)** — **pairs** is an array of `[value, expectedTypes]`, where **expectedTypes** is a string or array of strings (e.g. `'string'`, `['number','undefined']`). Uses built-in `typeof` checks plus **o.specialTypes**. On failure: throws (default) or returns an Error if **safe** is true. On success: returns `true`.
|
|
447
|
+
- **o.safeVerify(pairs)** — Same as **o.verify(pairs, true)**; returns `true` or `false` (no throw).
|
|
448
|
+
- **o.specialTypes** — Object of custom validators used by **o.verify()**. Built-in: `notEmptyString`, `array`, `promise`. **Developers can add global validators here**: assign a function `(value, typeofValue) => boolean` to **o.specialTypes.myType**. That validator is then available everywhere—in your app and inside Objs—so you can use `o.verify([x, ['myType']])` consistently.
|
|
243
449
|
|
|
244
|
-
`o
|
|
450
|
+
`o.showErrors` – false as default, but all errors are saved in **o.errors[]**.
|
|
245
451
|
|
|
246
|
-
`o
|
|
452
|
+
`o.errors` – an array of all errors.
|
|
247
453
|
|
|
248
|
-
`o
|
|
454
|
+
`o.logErrors()` – log all hidden errors in console.
|
|
249
455
|
|
|
250
|
-
`o
|
|
456
|
+
`o.onError` – set a function that is called when an error happens.
|
|
251
457
|
|
|
252
|
-
`o
|
|
458
|
+
`o.getStates()` – returns array of state info per init.
|
|
253
459
|
|
|
254
|
-
`o
|
|
460
|
+
`o.getStores()` – returns array of store refs.
|
|
255
461
|
|
|
256
|
-
`o
|
|
462
|
+
`o.getListeners()` – returns array of listener refs.
|
|
257
463
|
|
|
258
|
-
`o().
|
|
464
|
+
`o.camelToKebab(str)`, `o.kebabToCamel(str)` – convert between naming conventions.
|
|
259
465
|
|
|
260
|
-
|
|
261
|
-
`o().forEach(function)` – runs **function** with an object as the first parameter: {o, self, i, el} where is o-function, self Objs object, i-index of current element and el - DOM element.
|
|
466
|
+
`o.C(obj, key)` – safe `Object.hasOwn`-style check; returns whether `obj` has own property `key`. Used internally; available for app code. `o.F` and `o.U` are internal constants (false, undefined). `o.W` and `o.H` exist but are reserved; do not rely on them.
|
|
262
467
|
|
|
263
|
-
#### Events
|
|
264
|
-
`o().on(events, function, [options])` – adds **events** listeners separated by ', ' to elements.
|
|
265
468
|
|
|
266
|
-
`o().off(events, function, [options])` – removes **events** listeners separated by ', ' to elements.
|
|
267
469
|
|
|
268
|
-
`o().offAll([event])` – removes all listeners or for special **event** from elements.
|
|
269
470
|
|
|
270
|
-
|
|
471
|
+
## Why Objs for AI-assisted development
|
|
271
472
|
|
|
272
|
-
|
|
473
|
+
### The complete loop in one script
|
|
273
474
|
|
|
274
|
-
|
|
275
|
-
|
|
475
|
+
```
|
|
476
|
+
develop → o.autotag / o.reactQA → o.startRecording() → o.stopRecording()
|
|
477
|
+
→ o.exportPlaywrightTest() → paste → npx playwright test
|
|
478
|
+
```
|
|
276
479
|
|
|
277
|
-
|
|
480
|
+
No Playwright config to set up manually. No test IDs to maintain. The entire pipeline — component, QA tag, behavior capture, and Playwright test generation — runs inside the same library. Works in React projects too: add one script tag, sprinkle `{...o.reactQA('MyComponent')}`, record.
|
|
278
481
|
|
|
279
|
-
|
|
482
|
+
### Dev/prod build split
|
|
280
483
|
|
|
281
|
-
`
|
|
484
|
+
`objs.js` is the source for development. `objs.built.js` and `objs.built.min.js` are produced by `node build.js` (ESM + window.o). Only the debug flag is behind `__DEV__`.
|
|
485
|
+
|
|
486
|
+
The **recording pipeline** (`startRecording`, `stopRecording`, `exportTest`, `exportPlaywrightTest`, `reactQA`) ships in all builds so QA assessors can use it on staging.
|
|
487
|
+
|
|
488
|
+
Bundlers pick the right file automatically via `package.json` exports conditions:
|
|
489
|
+
|
|
490
|
+
```js
|
|
491
|
+
// Vite, webpack, esbuild — no config needed
|
|
492
|
+
import o from 'objs-core'; // dev server → objs.js, build → objs.built.js
|
|
493
|
+
|
|
494
|
+
// Script tag — explicit choice
|
|
495
|
+
<script src="objs.js"></script> // dev/staging
|
|
496
|
+
<script src="objs.built.js"></script> // or objs.built.min.js
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### States as AI-natural data structures
|
|
500
|
+
|
|
501
|
+
Every Objs component is a plain JS object. An LLM can generate correct components from a description without knowing JSX, virtual DOM, or React lifecycle rules:
|
|
502
|
+
|
|
503
|
+
```js
|
|
504
|
+
// AI prompt: "create a counter with increment and reset"
|
|
505
|
+
const counterStates = {
|
|
506
|
+
name: 'Counter',
|
|
507
|
+
render: { tag: 'div', html: '<span class="n">0</span> <button class="inc">+</button> <button class="rst">Reset</button>' },
|
|
508
|
+
updateCount: ({ self }, n) => { o(self).first('.n').html(n); },
|
|
509
|
+
};
|
|
510
|
+
const counter = o.init(counterStates).render().appendInside('#app');
|
|
511
|
+
o(counter).first('.inc').on('click', () => counter.updateCount(++n));
|
|
512
|
+
o(counter).first('.rst').on('click', () => counter.updateCount(n = 0));
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
No compiler. No build step to try the above. No framework knowledge needed to generate it.
|
|
516
|
+
|
|
517
|
+
### Granular reactive updates — no virtual DOM diff
|
|
518
|
+
|
|
519
|
+
Each store subscription calls exactly one targeted DOM write:
|
|
520
|
+
|
|
521
|
+
```js
|
|
522
|
+
// React with Redux: entire subtree re-renders, React diffs it
|
|
523
|
+
// Objs: one function call, one innerHTML assignment
|
|
524
|
+
o.connectRedux(store, s => s.userName, profileCard, 'updateName');
|
|
525
|
+
o.connectRedux(store, s => s.score, profileCard, 'updateScore');
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
Similar philosophy to Solid.js signals — but the update logic is a plain function, not a reactive primitive. An AI generates it without any framework knowledge.
|
|
529
|
+
|
|
530
|
+
### Comparison
|
|
531
|
+
|
|
532
|
+
| | Objs v2.0 | React ecosystem |
|
|
533
|
+
|---|---|---|
|
|
534
|
+
| **Setup** | `<script src="objs.js">` or `npm i objs-core` | React + Babel/Vite + config |
|
|
535
|
+
| **State management** | Built-in states + loaders | Redux / Zustand / MobX (separate) |
|
|
536
|
+
| **Routing** | `o.route()` built-in | React Router (separate) |
|
|
537
|
+
| **Testing** | Built-in `o.test()` + recording | Jest + Testing Library + Playwright |
|
|
538
|
+
| **Dev tools** | Built-in overlay, recording | React DevTools extension |
|
|
539
|
+
| **TypeScript** | `objs.d.ts` included | @types/react + separate config |
|
|
540
|
+
| **SSR** | Built-in DocumentMVP | Next.js / separate hydration setup |
|
|
541
|
+
| **AI context size** | ~2500 lines, one file | Dozens of packages, thousands of files |
|
|
542
|
+
| **Prod bundle overhead** | Dev code fully stripped | Depends on tree-shaking config |
|
|
543
|
+
|
|
544
|
+
### Real-world patterns
|
|
545
|
+
|
|
546
|
+
See [EXAMPLES.md](EXAMPLES.md) for complete runnable examples:
|
|
547
|
+
- Site menu with active route state
|
|
548
|
+
- Product card list + cart with shared store (granular update pattern)
|
|
549
|
+
- Overlay dialog with UTM auto-open and session persistence
|
|
550
|
+
- Drawer with filters and two-way URL sync
|
|
551
|
+
- Complex form with per-field validation and live preview
|
|
552
|
+
- React coexistence with shared context bridge
|
|
282
553
|
|
|
283
554
|
|
|
284
555
|
|