Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
114 commits
Select commit Hold shift + click to select a range
857bcc8
feat(pos-switch): if-property filters by some-value-eq
jg10-mastodon-social Apr 18, 2026
79dcf1c
feat(pos-switch): if-rev filters by some-value-eq
jg10-mastodon-social Apr 18, 2026
2fddd40
feat(pos-switch): compareValue supports every-value-eq
jg10-mastodon-social Apr 18, 2026
4e3f80c
feat(Thing): add basic observeLiterals
jg10-mastodon-social Apr 18, 2026
b3408e8
feat(Thing): observeLiterals updates after additions
jg10-mastodon-social Apr 18, 2026
6317ee6
feat(Thing): observeLiterals pushes updates in groups
jg10-mastodon-social Apr 18, 2026
9c2e43d
feat(Thing): observeLiterals ignores statements about other resources
jg10-mastodon-social Apr 18, 2026
d673951
feat(Thing): observeLiterals does not push if literals have not changed
jg10-mastodon-social Apr 18, 2026
c6fb238
feat(pos-switch): if-property takes literals into account
jg10-mastodon-social Apr 18, 2026
59e3274
chore(Thing): lint and typecheck
jg10-mastodon-social Apr 18, 2026
9614855
feat(pos-switch): compareValue supports (some|every)-value-(eq|gt|gte…
jg10-mastodon-social Apr 18, 2026
44c8883
feat(pos-switch): compareValue supports numbers
jg10-mastodon-social Apr 18, 2026
453edf9
test(pos-switch): add value-based example to integration test
jg10-mastodon-social Apr 26, 2026
146d08d
doc(pos-case): add value testing attributes
jg10-mastodon-social Apr 27, 2026
af1d7e8
doc(Thing): add doc string for observeLiterals
jg10-mastodon-social Apr 27, 2026
d727826
doc(core): add observeLiterals to changelog
jg10-mastodon-social Apr 27, 2026
85bd2a7
doc(elements): add pos-switch value testing to changelog
jg10-mastodon-social Apr 27, 2026
60b2865
doc: add pos-switch value testing to storybook
jg10-mastodon-social Apr 27, 2026
98f9d8b
refactor: generalize function to observe changes
angelo-v May 20, 2026
a86e62a
test(pos-switch): negation (absense) of property or backward link
angelo-v May 20, 2026
5e30a6e
test(pos-switch): prepare data driven test for several conditions
angelo-v May 20, 2026
bfa7ba0
test(pos-switch): add more test cases, including failing ones highlig…
angelo-v May 20, 2026
a3feea2
fix(core): export type Quad to prevent implicit type any
angelo-v May 27, 2026
42db1aa
fix(pos-switch): not is applied after comparing values
jg10-mastodon-social May 31, 2026
8313c0a
test(pos-switch): not if-property some-value-eq passes
jg10-mastodon-social May 31, 2026
906eb74
test(pos-switch): group tests; extract tests for test method
jg10-mastodon-social May 31, 2026
5e97f07
test(pos-switch): group reactivity tests
jg10-mastodon-social May 31, 2026
4781bc5
test(pos-switch): test if-typeof attribute combinations
jg10-mastodon-social May 31, 2026
b1e8f99
test(pos-switch): handle type checks with multiple values
jg10-mastodon-social May 31, 2026
bea4968
test(pos-switch): if-property presence/absence combinatorial tests
jg10-mastodon-social May 31, 2026
836237d
test(pos-switch): if-rev presence/absence combinatorial tests
jg10-mastodon-social May 31, 2026
f109741
test(pos-switch): combinatorial tests for property and rev with singl…
jg10-mastodon-social May 31, 2026
d978b36
test(pos-switch) combinatorial tests for property and rev with no values
jg10-mastodon-social May 31, 2026
7b2c8e5
test(pos-switch): combinatorial tests for property value with single …
jg10-mastodon-social May 31, 2026
8ef1126
test(pos-switch): combinatorial tests for property value with single …
jg10-mastodon-social May 31, 2026
f5af4ad
chore(core): rebase CHANGELOG
jg10-mastodon-social May 31, 2026
3f30099
refactor(pos-switch): extract part of the logic to own file and function
angelo-v Jun 10, 2026
e9ba07d
feat: pos-case provides the rule configured through its attributes
angelo-v Jul 14, 2026
bca4157
chore: upgrade stencil vitest
angelo-v Jul 14, 2026
2d65b8b
refactor: prepare for rule evaluation using the rules from case elements
angelo-v Jul 14, 2026
d94413c
refactor: use the new rule definitions to cover the if-typeof case
angelo-v Jul 14, 2026
5dfc3c2
fix: resets and updates when resource is changed without leaving stri…
angelo-v Jul 14, 2026
4e604ff
refactor: extract function to test a rule based on a given context
angelo-v Jul 14, 2026
a98d4d0
refactor: extract functions to test all rules
angelo-v Jul 14, 2026
6722ace
fix: export type Quad from store
angelo-v Jul 14, 2026
067979e
fix: initialize non-nullable values and be explicit about nullable ones
angelo-v Jul 14, 2026
e50da08
test: add unit tests for doesRuleMatch and if-typeof rule
angelo-v Jul 14, 2026
ef9a8cc
feat: respect negation when evaluating a rule
angelo-v Jul 14, 2026
bfb4b99
feat: pos-case negates a rule if not prop is set
angelo-v Jul 14, 2026
a90a19c
refactor(pos-case): use own properties instead of looking at the dom …
angelo-v Jul 14, 2026
f279643
test: extract common data to describe block
angelo-v Jul 14, 2026
21942fd
feat: doesRuleMatch checks if-property rule
angelo-v Jul 14, 2026
5c1c674
test: negated if-property rule
angelo-v Jul 14, 2026
4c774b1
feat: add if-rev rule
angelo-v Jul 14, 2026
b89fb80
refactor: introduce explicit types for each rule to get rid of null-c…
angelo-v Jul 14, 2026
81e1228
feat(degradation!): cover all basic rule types (without values and wi…
angelo-v Jul 14, 2026
daf449b
test: find matching rules returns all rules that match
angelo-v Jul 14, 2026
d7b3f18
feat: pos-case provides else-if rules
angelo-v Jul 14, 2026
3aa9590
feat: evaluate if-else branches
angelo-v Jul 14, 2026
4062305
feat: evaluate plain else branches
angelo-v Jul 14, 2026
d876895
fix: prevent deadlock situation where pos-switch waits for the pos-ca…
angelo-v Jul 14, 2026
9a6ff65
feat: check if-property some-value-eq
angelo-v Jul 15, 2026
0f3d9cc
feat: pos-case provides some-value-eq / every-value-eq comparison for…
angelo-v Jul 15, 2026
067cfc3
feat: pos-case provides some-value-gt / every-value-gt comparison for…
angelo-v Jul 15, 2026
b37f57a
feat: pos-case provides some-value-gte / every-value-gte comparison f…
angelo-v Jul 15, 2026
d097b96
refactor: simplify finding the right comparison to apply
angelo-v Jul 15, 2026
2a3438a
feat: pos-case provides lt/lte comparisons for both if-property and i…
angelo-v Jul 15, 2026
0e33714
feat: check if-rev rule with value matching
angelo-v Jul 15, 2026
2eace66
feat: check if-property rule with specific relation
angelo-v Jul 15, 2026
3556ccc
feat: respect both literal values and relation uris when matching for…
angelo-v Jul 15, 2026
2816256
refactor: move stuff from logic.ts into the rules module and add docu…
angelo-v Jul 15, 2026
b4900d6
refactor: rename method to better match the naming pattern of the cal…
angelo-v Jul 15, 2026
76d50b0
test: do values match for some-eq & every-eq
angelo-v Jul 15, 2026
27e506a
feat: support numeric values
angelo-v Jul 15, 2026
a3d18f3
test: do values match for some-gt
angelo-v Jul 15, 2026
1958a3f
test: do values match for every-gt
angelo-v Jul 15, 2026
97b9d42
test: do values match for greater than or equal
angelo-v Jul 15, 2026
1a4cfdf
test: do values match: cover all permutations with parameterized test…
angelo-v Jul 15, 2026
a7cbfd1
chore: install vitest coverage
angelo-v Jul 15, 2026
3a180f0
chore: remove obsolete test (fully covered by vitest variant)
angelo-v Jul 15, 2026
8a245b2
chore: fix typo
angelo-v Jul 15, 2026
7e9be04
fix: every-value-* is false if property/rev is not present at all
angelo-v Jul 15, 2026
b6b0ddb
test: re-enable the old jest test with a lot of conditions as an addi…
angelo-v Jul 15, 2026
083fd74
fix: target value is actually always a string and gets converted to n…
angelo-v Jul 15, 2026
7a550f4
fix: imports
angelo-v Jul 15, 2026
c7d8652
test: fix test grouping
angelo-v Jul 16, 2026
8608ca2
fix(pos-switch): renders the else case if it is the only case present
angelo-v Jul 16, 2026
7e81848
fix: keep track of if-else scopes to correlate and else branch with t…
angelo-v Jul 16, 2026
5bf7c70
test: integration test for value matching
angelo-v Jul 16, 2026
600c392
test: integration test to ensure cases are re-evaluated when resource…
angelo-v Jul 16, 2026
45c03d9
test: "if -> else if -> else" rule combination
angelo-v Jul 16, 2026
1b83bdf
test: "if -> else if, if -> else if" rule combination
angelo-v Jul 16, 2026
20357d6
test: "if -> else, if -> else" rule combination
angelo-v Jul 16, 2026
bf12b9a
test: rule combinations with negations "if not -> else if not -> else"
angelo-v Jul 16, 2026
543500a
test: integration test rule negation
angelo-v Jul 16, 2026
dc487ff
test: integration test greater / less than comparisons
angelo-v Jul 16, 2026
2910402
test(pos-case): provide else and not for all rule types
angelo-v Jul 16, 2026
52e7b55
test: add missing test for if-typeof when only the wrong type is present
angelo-v Jul 16, 2026
829717b
test: add missing test for if-property presence
angelo-v Jul 16, 2026
cf51f8a
test: add missing test for if-rev presence
angelo-v Jul 16, 2026
fc8074c
test: throw error for unknown semantic
angelo-v Jul 16, 2026
62f009a
test: negation of if-property with specific value
angelo-v Jul 16, 2026
e6e304c
test: negation of if-property with specific relation
angelo-v Jul 16, 2026
3142a1a
test: negation of if-rev with specific reverse relation
angelo-v Jul 16, 2026
71b8445
test: add integration test to verify the cases update after data has …
angelo-v Jul 16, 2026
c641980
chore: remove old obsolete test suite
angelo-v Jul 16, 2026
c76c395
refactor: determine by rule which data to observe, instead of checkin…
angelo-v Jul 16, 2026
0c6e22d
test: add tests for all observed values (types, literals, relations, …
angelo-v Jul 17, 2026
0f1adeb
refactor: simplify subscribing to observed values.
angelo-v Jul 17, 2026
dc8f727
refactor: build the full rule context at once from relevant observed …
angelo-v Jul 17, 2026
cdd2a0e
feat: pos-case renders its own template based on an active state cont…
angelo-v Jul 17, 2026
83636fc
test: fix jsx config also for integration tests
angelo-v Jul 17, 2026
dc12e61
feat: show misconfigured pos-case rules to the user
angelo-v Jul 17, 2026
a3332a3
feat: add some basic styling to errors to distinguish them from norma…
angelo-v Jul 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 0.32.0

### Added

- [`Thing.observeLiterals`](https://pod-os.org/reference/core/classes/thing/#observeliterals) pushes changes to literals

## 0.31.0

### Breaking changes
Expand Down Expand Up @@ -33,7 +39,7 @@ and this project adheres to

## 0.28.0

- [`addRelation`](https://pod-os.org/reference/core/classes/podos/#addrelation: A method to add a relation (link) between things
- [`addRelation`](https://pod-os.org/reference/core/classes/podos/#addrelation): A method to add a relation (link) between things

## 0.27.0

Expand Down
1 change: 1 addition & 0 deletions core/src/Store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import {
Term,
} from "rdflib/lib/tf-types";
import { iana, internal } from "./namespaces";
export { Quad } from "rdflib/lib/tf-types";

/**
* The Store contains all data that is known locally.
Expand Down
214 changes: 203 additions & 11 deletions core/src/thing/Thing.literals.spec.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { beforeEach, describe, expect, it } from "vitest";
import { blankNode, graph, IndexedFormula, sym } from "rdflib";
import { afterEach, beforeEach, describe, expect, it, Mock, vi } from "vitest";
import { blankNode, graph, IndexedFormula, literal, quad, sym } from "rdflib";
import { PodOsSession } from "../authentication";
import { Thing } from "./Thing";
import { Literal, Thing } from "./Thing";
import { Store } from "../Store";
import { Observable } from "rxjs";

describe("Thing", function () {
describe("literals", () => {
let internalStore: IndexedFormula;
const mockSession = {} as unknown as PodOsSession;
let store: Store;
let internalStore: IndexedFormula;
const mockSession = {} as unknown as PodOsSession;
let store: Store;

beforeEach(() => {
internalStore = graph();
store = new Store(mockSession, undefined, undefined, internalStore);
});
beforeEach(() => {
internalStore = graph();
store = new Store(mockSession, undefined, undefined, internalStore);
});

describe("literals", () => {
it("are empty, if store is empty", () => {
const it = new Thing(
"https://jane.doe.example/container/file.ttl#fragment",
Expand Down Expand Up @@ -162,4 +163,195 @@ describe("Thing", function () {
]);
});
});

describe("observeLiterals", () => {
beforeEach(() => {
vi.useFakeTimers();
});

afterEach(() => {
vi.useRealTimers();
});

let uri: string,
subscriber: Mock,
thing: Thing,
literalsSpy: Mock,
observable: Observable<Literal[]>;

beforeEach(() => {
// Given a store with statements about a URI
uri = "https://jane.doe.example/container/file.ttl#fragment";
internalStore.add(
sym(uri),
sym("http://vocab.test/first"),
"value 1-1",
sym(uri),
);
internalStore.add(
sym(uri),
sym("http://vocab.test/second"),
"value 2-1",
sym(uri),
);
internalStore.add(
sym(uri),
sym("http://vocab.test/second"),
"value 2-2",
sym(uri),
);
internalStore.add(
sym(uri),
sym("http://vocab.test/third"),
"value 3-1",
sym(uri),
);

// and a Thing with a literals method
subscriber = vi.fn();
thing = new Thing(uri, store);
literalsSpy = vi.spyOn(thing, "literals");

// and a subscription to changes in relations
observable = thing.observeLiterals();
observable.subscribe(subscriber);
});

it("pushes existing literals immediately", () => {
expect(subscriber).toHaveBeenCalledTimes(1);
expect(literalsSpy).toHaveBeenCalledTimes(1);
expect(subscriber.mock.calls).toEqual([
[
[
{
predicate: "http://vocab.test/first",
label: "first",
values: ["value 1-1"],
},
{
predicate: "http://vocab.test/second",
label: "second",
values: ["value 2-1", "value 2-2"],
},
{
predicate: "http://vocab.test/third",
label: "third",
values: ["value 3-1"],
},
],
],
]);
});

it("updates after removals", () => {
internalStore.removeStatement(
quad(
sym(uri),
sym("http://vocab.test/first"),
literal("value 1-1"),
sym(uri),
),
);
vi.advanceTimersByTime(250);
expect(subscriber).toHaveBeenCalledTimes(2);
expect(literalsSpy).toHaveBeenCalledTimes(2);
expect(subscriber.mock.lastCall).toEqual([
[
{
predicate: "http://vocab.test/second",
label: "second",
values: ["value 2-1", "value 2-2"],
},
{
predicate: "http://vocab.test/third",
label: "third",
values: ["value 3-1"],
},
],
]);
});

it("updates after additions", () => {
internalStore.add(sym(uri), sym("http://vocab.test/first"), "value 1-2");
vi.advanceTimersByTime(250);
expect(subscriber).toHaveBeenCalledTimes(2);
expect(literalsSpy).toHaveBeenCalledTimes(2);
expect(subscriber.mock.lastCall).toEqual([
[
{
predicate: "http://vocab.test/first",
label: "first",
values: ["value 1-1", "value 1-2"],
},
{
predicate: "http://vocab.test/second",
label: "second",
values: ["value 2-1", "value 2-2"],
},
{
predicate: "http://vocab.test/third",
label: "third",
values: ["value 3-1"],
},
],
]);
});

it("pushes update in groups", () => {
internalStore.removeStatement(
quad(
sym(uri),
sym("http://vocab.test/first"),
literal("value 1-1"),
sym(uri),
),
);
internalStore.add(sym(uri), sym("http://vocab.test/first"), "value 1-2");
internalStore.add(sym(uri), sym("http://vocab.test/third"), "value 3-2");
vi.advanceTimersByTime(250);
expect(subscriber).toHaveBeenCalledTimes(2);
expect(literalsSpy).toHaveBeenCalledTimes(2);
expect(subscriber.mock.lastCall).toEqual([
[
{
predicate: "http://vocab.test/second",
label: "second",
values: ["value 2-1", "value 2-2"],
},
{
predicate: "http://vocab.test/third",
label: "third",
values: ["value 3-1", "value 3-2"],
},
{
predicate: "http://vocab.test/first",
label: "first",
values: ["value 1-2"],
},
],
]);
});

it("ignores irrelevant statements about other resources", () => {
internalStore.add(
sym("https://other.uri/"),
sym("http://vocab.test/first"),
"value",
);
vi.advanceTimersByTime(250);
expect(subscriber).toHaveBeenCalledTimes(1);
expect(literalsSpy).toHaveBeenCalledTimes(1);
});

it("does not push if literals have not changed", () => {
internalStore.add(
sym(uri),
sym("http://vocab.test/first"),
sym("http://not.a.literal/"),
);
vi.advanceTimersByTime(250);
expect(literalsSpy).toHaveBeenCalledTimes(2);
expect(subscriber).toHaveBeenCalledTimes(1);
});
});
});
Loading
Loading