Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
9770768
feat(pos-switch): if-property filters by some-value-eq
jg10-mastodon-social Apr 18, 2026
aead175
feat(pos-switch): if-rev filters by some-value-eq
jg10-mastodon-social Apr 18, 2026
598bb76
feat(pos-switch): compareValue supports every-value-eq
jg10-mastodon-social Apr 18, 2026
1eca2ad
feat(Thing): add basic observeLiterals
jg10-mastodon-social Apr 18, 2026
d0ae499
feat(Thing): observeLiterals updates after additions
jg10-mastodon-social Apr 18, 2026
205d72a
feat(Thing): observeLiterals pushes updates in groups
jg10-mastodon-social Apr 18, 2026
688e391
feat(Thing): observeLiterals ignores statements about other resources
jg10-mastodon-social Apr 18, 2026
57e2fd6
feat(Thing): observeLiterals does not push if literals have not changed
jg10-mastodon-social Apr 18, 2026
0d9df94
feat(pos-switch): if-property takes literals into account
jg10-mastodon-social Apr 18, 2026
8887377
chore(Thing): lint and typecheck
jg10-mastodon-social Apr 18, 2026
a106af2
feat(pos-switch): compareValue supports (some|every)-value-(eq|gt|gte…
jg10-mastodon-social Apr 18, 2026
857d5d1
feat(pos-switch): compareValue supports numbers
jg10-mastodon-social Apr 18, 2026
d9c0313
test(pos-switch): add value-based example to integration test
jg10-mastodon-social Apr 26, 2026
57bccaa
doc(pos-case): add value testing attributes
jg10-mastodon-social Apr 27, 2026
fc421db
doc(Thing): add doc string for observeLiterals
jg10-mastodon-social Apr 27, 2026
e4e3952
doc(core): add observeLiterals to changelog
jg10-mastodon-social Apr 27, 2026
101b86c
doc(elements): add pos-switch value testing to changelog
jg10-mastodon-social Apr 27, 2026
27891d7
doc: add pos-switch value testing to storybook
jg10-mastodon-social Apr 27, 2026
36ef2e1
refactor: generalize function to observe changes
angelo-v May 20, 2026
afa327b
test(pos-switch): negation (absense) of property or backward link
angelo-v May 20, 2026
3d6a895
test(pos-switch): prepare data driven test for several conditions
angelo-v May 20, 2026
e66d556
test(pos-switch): add more test cases, including failing ones highlig…
angelo-v May 20, 2026
edac760
fix(core): export type Quad to prevent implicit type any
angelo-v May 27, 2026
69f0119
fix(pos-switch): not is applied after comparing values
jg10-mastodon-social May 31, 2026
148570d
test(pos-switch): not if-property some-value-eq passes
jg10-mastodon-social May 31, 2026
b58bd02
test(pos-switch): group tests; extract tests for test method
jg10-mastodon-social May 31, 2026
3384a2e
test(pos-switch): group reactivity tests
jg10-mastodon-social May 31, 2026
83c6eaa
test(pos-switch): test if-typeof attribute combinations
jg10-mastodon-social May 31, 2026
699f066
test(pos-switch): handle type checks with multiple values
jg10-mastodon-social May 31, 2026
fcfa10f
test(pos-switch): if-property presence/absence combinatorial tests
jg10-mastodon-social May 31, 2026
7fa04f3
test(pos-switch): if-rev presence/absence combinatorial tests
jg10-mastodon-social May 31, 2026
958ffde
test(pos-switch): combinatorial tests for property and rev with singl…
jg10-mastodon-social May 31, 2026
bbb8943
test(pos-switch) combinatorial tests for property and rev with no values
jg10-mastodon-social May 31, 2026
d6b03d6
test(pos-switch): combinatorial tests for property value with single …
jg10-mastodon-social May 31, 2026
4e5a20a
test(pos-switch): combinatorial tests for property value with single …
jg10-mastodon-social May 31, 2026
9bbd066
chore(core): rebase CHANGELOG
jg10-mastodon-social May 31, 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.30.0
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is now 0.30.0, but I may have misunderstood what is in the previous release


### Added

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

## 0.29.0

### Added
Expand All @@ -16,7 +22,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 @@ -43,6 +43,7 @@ import {
Quad_Subject,
Term,
} from "rdflib/lib/tf-types";
export type { Quad } from "rdflib/lib/tf-types";

/**
* The Store contains all data that is known locally.
Expand Down
212 changes: 202 additions & 10 deletions core/src/thing/Thing.literals.spec.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import { blankNode, graph, IndexedFormula, sym } from "rdflib";
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 @@ -161,4 +162,195 @@ describe("Thing", function () {
]);
});
});

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

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

let uri: string,
subscriber: jest.Mock,
thing: Thing,
literalsSpy: jest.SpyInstance,
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 = jest.fn();
thing = new Thing(uri, store);
literalsSpy = jest.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),
),
);
jest.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");
jest.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");
jest.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",
);
jest.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/"),
);
jest.advanceTimersByTime(250);
expect(literalsSpy).toHaveBeenCalledTimes(2);
expect(subscriber).toHaveBeenCalledTimes(1);
});
});
});
Loading
Loading