Skip to content

Commit adc7bd5

Browse files
committed
Merge pull request #36 from silky-x0/test/swap-source
Added Unit Test for swap-source.ts
2 parents f4bb38c + f248b5a commit adc7bd5

File tree

3 files changed

+240
-0
lines changed

3 files changed

+240
-0
lines changed

QUICKSTART.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
## Quick Start
2+
3+
### Installation
4+
5+
```bash
6+
# Using npm
7+
npm install rimmel rxjs
8+
9+
# Using yarn
10+
yarn add rimmel rxjs
11+
12+
# Using pnpm
13+
pnpm add rimmel rxjs
14+
```
15+
16+
### Basic Usage
17+
18+
```js
19+
import { BehaviorSubject, scan } from 'rxjs';
20+
import { rml } from 'rimmel';
21+
22+
const Component = () => {
23+
const count = new BehaviorSubject(0).pipe(
24+
scan(x=>x+1)
25+
);
26+
27+
return rml`
28+
<button onclick="${count}">
29+
click me (${count})
30+
</button>
31+
`;
32+
};
33+
34+
const App = () => {
35+
return rml`
36+
<h1>Hello <img class="icon" src="https://rimmel.js.org/assets/rimmel.png"> World</h1>
37+
${Component()}
38+
39+
<hr>
40+
Starter for <a href="https://github.com/reactivehtml/rimmel" target="_blank">Rimmel.js</a>
41+
`;
42+
};
43+
44+
document.body.innerHTML = App();
45+
```
46+
47+
## Key Features
48+
49+
- **Stream-First Architecture**: Everything is a stream - events, data, UI updates
50+
- **No Virtual DOM**: Direct DOM updates via optimized "sinks"
51+
- **Tiny Bundle Size**: Core is just 2.5KB, tree-shakeable imports
52+
- **Zero Build Requirements**: Works with plain JavaScript
53+
- **Automatic Memory Management**: Handles subscription cleanup
54+
- **Built-in Suspense**: Automatic loading states with BehaviorSubject
55+
- **Web Components Support**: Create custom elements easily
56+
- **TypeScript Support**: Full type definitions included
57+
58+
## Core Concepts
59+
60+
### Sources (Input)
61+
- DOM Events (`onclick`, `onmousemove`, etc)
62+
- Promises
63+
- RxJS Observables
64+
- Custom Event Sources
65+
66+
### Sinks (Output)
67+
- DOM Updates
68+
- Class Management
69+
- Style Updates
70+
- Attribute Changes
71+
- Custom Sinks
72+
73+
## Available Sinks
74+
75+
The library includes specialized sinks for common UI operations:
76+
77+
- `InnerHTML` - Update element content
78+
- `InnerText` - Safe text updates
79+
- `Class` - Manage CSS classes
80+
- `Style` - Update styles
81+
- `Value` - Form input values
82+
- `Disabled` - Toggle disabled state
83+
- `Readonly` - Toggle readonly state
84+
- `Removed` - Remove elements
85+
- `Sanitize` - Safe HTML rendering
86+
- `AppendHTML` - Append content
87+
- `PrependHTML` - Prepend content
88+
89+
## Development
90+
91+
```bash
92+
# Install dependencies
93+
npm install
94+
95+
# Run tests
96+
npm run test
97+
98+
# Build library
99+
npm run build
100+
101+
# Run demo app
102+
npm run kitchen-sink
103+
```
104+
105+
## Examples
106+
107+
Check out our examples:
108+
109+
- [Basic Demos](https://stackblitz.com/@dariomannu/collections/rimmel-js-getting-started)
110+
- [Advanced Patterns](https://stackblitz.com/@dariomannu/collections/rimmel-js-experiments)
111+
- [Web Components](https://stackblitz.com/@dariomannu/collections/web-components)
112+
- [Web Workers](https://stackblitz.com/@dariomannu/collections/web-workers)

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Rimmel is a powerful, fast and lightweight JavaScript UI library for creating we
1010

1111
It implements [RML](https://github.com/ReactiveHTML/reactive-markup), the Reactive Markup which makes your HTML work with Streams in a seamless way.
1212

13+
## To Busy to read?, Check [TL;DR 📜](./QUICKSTART.md)<br>
14+
1315
## Getting started
1416
If you are new to reactive streams, there is a [3m crash-course](https://medium.com/@fourtyeighthours/the-mostly-inaccurate-crash-course-for-reactive-ui-development-w-rxjs-ddbb7e5e526e) tailored for UI development with Rimmel, arguably the simplest RxJS introduction around to get you started.
1517

src/sources/swap-source.test.ts

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import type { Observable } from 'rxjs';
2+
3+
import { Subject } from 'rxjs';
4+
import { MockElement, MockEvent } from '../test-support';
5+
import { Swap, swap } from './swap-source';
6+
7+
describe('Swap Event Adapter', () => {
8+
9+
it('Swaps a value in an element with a static string', () => {
10+
const oldValue = 'old data';
11+
const newValue = 'new data';
12+
13+
const el = MockElement({
14+
tagName: 'INPUT',
15+
type: 'text',
16+
value: oldValue,
17+
});
18+
19+
const eventData = MockEvent('input', {
20+
target: el as HTMLInputElement
21+
});
22+
23+
const handlerSpy = jest.fn();
24+
const source = Swap(newValue)(handlerSpy);
25+
source.next(eventData);
26+
27+
expect(handlerSpy).toHaveBeenCalledWith(oldValue);
28+
expect(el.value).toEqual(newValue);
29+
});
30+
31+
it('Swaps a value in an element using a function', () => {
32+
const oldValue = 'old data';
33+
34+
const el = MockElement({
35+
tagName: 'INPUT',
36+
type: 'text',
37+
value: oldValue,
38+
});
39+
40+
const eventData = MockEvent('input', {
41+
target: el as HTMLInputElement
42+
});
43+
44+
const replaceFn = (v: string) => v.toUpperCase();
45+
46+
const handlerSpy = jest.fn();
47+
const source = Swap(replaceFn)(handlerSpy);
48+
source.next(eventData);
49+
50+
expect(handlerSpy).toHaveBeenCalledWith(oldValue);
51+
expect(el.value).toEqual('OLD DATA');
52+
});
53+
54+
it('Swaps a value in an element with empty string by default', () => {
55+
const oldValue = 'old data';
56+
57+
const el = MockElement({
58+
tagName: 'INPUT',
59+
type: 'text',
60+
value: oldValue,
61+
});
62+
63+
const eventData = MockEvent('input', {
64+
target: el as HTMLInputElement
65+
});
66+
67+
const handlerSpy = jest.fn();
68+
const source = Swap()(handlerSpy);
69+
source.next(eventData);
70+
71+
expect(handlerSpy).toHaveBeenCalledWith(oldValue);
72+
expect(el.value).toEqual('');
73+
});
74+
75+
});
76+
77+
describe('swap Event Operator', () => {
78+
79+
it('Swaps and emits a value from an element with static string', () => {
80+
const oldValue = 'old data';
81+
const newValue = 'new data';
82+
83+
const el = MockElement({
84+
tagName: 'INPUT',
85+
type: 'text',
86+
value: oldValue,
87+
});
88+
89+
const eventData = MockEvent('input', {
90+
target: el as HTMLInputElement
91+
});
92+
93+
const handlerSpy = jest.fn();
94+
const pipeline = new Subject<typeof eventData>().pipe(swap(newValue)) as Observable<string> & Subject<typeof eventData>;
95+
pipeline.subscribe(x => handlerSpy(x));
96+
pipeline.next(eventData);
97+
98+
expect(handlerSpy).toHaveBeenCalledWith(oldValue);
99+
expect(el.value).toEqual(newValue);
100+
});
101+
102+
it('Swaps and emits a value from an element using function', () => {
103+
const oldValue = 'old data';
104+
105+
const el = MockElement({
106+
tagName: 'INPUT',
107+
type: 'text',
108+
value: oldValue,
109+
});
110+
111+
const eventData = MockEvent('input', {
112+
target: el as HTMLInputElement
113+
});
114+
115+
const replaceFn = (v: string) => v.toUpperCase();
116+
117+
const handlerSpy = jest.fn();
118+
const pipeline = new Subject<typeof eventData>().pipe(swap(replaceFn)) as Observable<string> & Subject<typeof eventData>;
119+
pipeline.subscribe(x => handlerSpy(x));
120+
pipeline.next(eventData);
121+
122+
expect(handlerSpy).toHaveBeenCalledWith(oldValue);
123+
expect(el.value).toEqual('OLD DATA');
124+
});
125+
126+
});

0 commit comments

Comments
 (0)