Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 28 additions & 0 deletions .changeset/6959-gantt-lazy-read-flake.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
---
---

Test-only: `ObjectGantt.referenceArms-6837.test.tsx` now reads the chart through an
asynchronous query instead of racing it. No published behaviour changes; no runtime code
was touched.

The block at `:257` mounted through the file's `mount()` helper, whose gate settles a MOCK
CALL — `find('task', { $expand })` was ISSUED — and then read the DOM synchronously with
`getByTestId('gantt-view')`. Those are two different facts one promise-resolution apart:
`ObjectGantt` flips `loading` false in the `finally` of `reload()`, i.e. after that find
RESOLVES, so on return from `mount()` the component can still be painting its
`Loading Gantt chart...` branch. The synchronous read raced it and lost twice in fifteen
minutes in the merge queue — a heavier environment than PR CI — ejecting two pull requests
that do not touch `plugin-gantt` at all, one of which passed and failed the queue on
byte-identical content.

The assertion is unchanged, and `mount()` keeps its find-call gate: the refusal probe above
this block needs proof of the SCHEMA-dependent commit, which the chart's presence alone does
not carry (`loading` flips false after the FIRST find resolves, before `objectSchema`
lands). The two gates measure different facts, so the block now waits for both rather than
trading one for the other.

Measured rather than assumed: with the data source's resolution delayed 500ms — the timing
FACT mutated, never the assertion — the old spelling throws
`Unable to find an element by: [data-testid="gantt-view"]` against a DOM reading exactly
`Loading Gantt chart...`, reproducing the queue failure, while the new spelling resolves
through that fallback and reads `data-count=2`.
Original file line numberDiff line numberDiff line change
Expand Up@@ -253,9 +253,25 @@ describe('ObjectGantt resolves only contract-declared target spellings (objectui
it('and degrades to the distinct loaded values rather than rendering nothing', async () => {
// Guards the refusal above against the degenerate pass: a gantt that
// rendered no quick filter at all would also never fetch `projects`.
//
// ⚠️ ASYNC reads, deliberately (objectui#6959). `mount()` settles on a
// MOCK CALL — `find('task', { $expand })` was ISSUED — and that is one
// promise resolution EARLIER than the `setLoading(false)` in `reload()`'s
// `finally`. So on return from `mount()` `ObjectGantt` may still be
// painting its `loading` branch (`Loading Gantt chart...`), and a
// SYNCHRONOUS `getByTestId` here races it. It lost that race twice in
// fifteen minutes in the merge queue — a heavier runner than PR CI —
// ejecting two PRs that do not touch `plugin-gantt` at all.
//
// `mount()`'s find-call gate STAYS: the refusal probe needs proof of the
// SCHEMA-dependent commit, and `gantt-view` alone does not carry it —
// `loading` flips false after the FIRST `find()` resolves, which happens
// before `objectSchema` lands. The two gates measure different facts, so
// this block waits for both rather than trading one for the other.
const { ds, view } = await mount(FIELD_DEFS.legacy_camel);
expect(view.getByTestId('gantt-view').getAttribute('data-count')).toBe('2');
fireEvent.click(view.getByTestId('quick-filter-trigger-project'));
const chart = await view.findByTestId('gantt-view');
expect(chart.getAttribute('data-count')).toBe('2');
fireEvent.click(await view.findByTestId('quick-filter-trigger-project'));
const panel = view.getByTestId('quick-filter-panel-project');
expect(within(panel).getByTestId('quick-filter-option-project-p1')).toBeTruthy();
expect(within(panel).queryByTestId('quick-filter-option-project-p3')).toBeNull();
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
test(plugin-gantt): read the chart through an async query, not a race by os-sam · Pull Request #6966 · objectstack-ai/objectui · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 28 additions & 0 deletions .changeset/6959-gantt-lazy-read-flake.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
---
---

Test-only: `ObjectGantt.referenceArms-6837.test.tsx` now reads the chart through an
asynchronous query instead of racing it. No published behaviour changes; no runtime code
was touched.

The block at `:257` mounted through the file's `mount()` helper, whose gate settles a MOCK
CALL — `find('task', { $expand })` was ISSUED — and then read the DOM synchronously with
`getByTestId('gantt-view')`. Those are two different facts one promise-resolution apart:
`ObjectGantt` flips `loading` false in the `finally` of `reload()`, i.e. after that find
RESOLVES, so on return from `mount()` the component can still be painting its
`Loading Gantt chart...` branch. The synchronous read raced it and lost twice in fifteen
minutes in the merge queue — a heavier environment than PR CI — ejecting two pull requests
that do not touch `plugin-gantt` at all, one of which passed and failed the queue on
byte-identical content.

The assertion is unchanged, and `mount()` keeps its find-call gate: the refusal probe above
this block needs proof of the SCHEMA-dependent commit, which the chart's presence alone does
not carry (`loading` flips false after the FIRST find resolves, before `objectSchema`
lands). The two gates measure different facts, so the block now waits for both rather than
trading one for the other.

Measured rather than assumed: with the data source's resolution delayed 500ms — the timing
FACT mutated, never the assertion — the old spelling throws
`Unable to find an element by: [data-testid="gantt-view"]` against a DOM reading exactly
`Loading Gantt chart...`, reproducing the queue failure, while the new spelling resolves
through that fallback and reads `data-count=2`.
Original file line numberDiff line numberDiff line change
Expand Up@@ -253,9 +253,25 @@ describe('ObjectGantt resolves only contract-declared target spellings (objectui
it('and degrades to the distinct loaded values rather than rendering nothing', async () => {
// Guards the refusal above against the degenerate pass: a gantt that
// rendered no quick filter at all would also never fetch `projects`.
//
// ⚠️ ASYNC reads, deliberately (objectui#6959). `mount()` settles on a
// MOCK CALL — `find('task', { $expand })` was ISSUED — and that is one
// promise resolution EARLIER than the `setLoading(false)` in `reload()`'s
// `finally`. So on return from `mount()` `ObjectGantt` may still be
// painting its `loading` branch (`Loading Gantt chart...`), and a
// SYNCHRONOUS `getByTestId` here races it. It lost that race twice in
// fifteen minutes in the merge queue — a heavier runner than PR CI —
// ejecting two PRs that do not touch `plugin-gantt` at all.
//
// `mount()`'s find-call gate STAYS: the refusal probe needs proof of the
// SCHEMA-dependent commit, and `gantt-view` alone does not carry it —
// `loading` flips false after the FIRST `find()` resolves, which happens
// before `objectSchema` lands. The two gates measure different facts, so
// this block waits for both rather than trading one for the other.
const { ds, view } = await mount(FIELD_DEFS.legacy_camel);
expect(view.getByTestId('gantt-view').getAttribute('data-count')).toBe('2');
fireEvent.click(view.getByTestId('quick-filter-trigger-project'));
const chart = await view.findByTestId('gantt-view');
expect(chart.getAttribute('data-count')).toBe('2');
fireEvent.click(await view.findByTestId('quick-filter-trigger-project'));
const panel = view.getByTestId('quick-filter-panel-project');
expect(within(panel).getByTestId('quick-filter-option-project-p1')).toBeTruthy();
expect(within(panel).queryByTestId('quick-filter-option-project-p3')).toBeNull();
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' test(plugin-gantt): read the chart through an async query, not a race by os-sam · Pull Request #6966 · objectstack-ai/objectui · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 28 additions & 0 deletions .changeset/6959-gantt-lazy-read-flake.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
---
---

Test-only: `ObjectGantt.referenceArms-6837.test.tsx` now reads the chart through an
asynchronous query instead of racing it. No published behaviour changes; no runtime code
was touched.

The block at `:257` mounted through the file's `mount()` helper, whose gate settles a MOCK
CALL — `find('task', { $expand })` was ISSUED — and then read the DOM synchronously with
`getByTestId('gantt-view')`. Those are two different facts one promise-resolution apart:
`ObjectGantt` flips `loading` false in the `finally` of `reload()`, i.e. after that find
RESOLVES, so on return from `mount()` the component can still be painting its
`Loading Gantt chart...` branch. The synchronous read raced it and lost twice in fifteen
minutes in the merge queue — a heavier environment than PR CI — ejecting two pull requests
that do not touch `plugin-gantt` at all, one of which passed and failed the queue on
byte-identical content.

The assertion is unchanged, and `mount()` keeps its find-call gate: the refusal probe above
this block needs proof of the SCHEMA-dependent commit, which the chart's presence alone does
not carry (`loading` flips false after the FIRST find resolves, before `objectSchema`
lands). The two gates measure different facts, so the block now waits for both rather than
trading one for the other.

Measured rather than assumed: with the data source's resolution delayed 500ms — the timing
FACT mutated, never the assertion — the old spelling throws
`Unable to find an element by: [data-testid="gantt-view"]` against a DOM reading exactly
`Loading Gantt chart...`, reproducing the queue failure, while the new spelling resolves
through that fallback and reads `data-count=2`.
Original file line numberDiff line numberDiff line change
Expand Up@@ -253,9 +253,25 @@ describe('ObjectGantt resolves only contract-declared target spellings (objectui
it('and degrades to the distinct loaded values rather than rendering nothing', async () => {
// Guards the refusal above against the degenerate pass: a gantt that
// rendered no quick filter at all would also never fetch `projects`.
//
// ⚠️ ASYNC reads, deliberately (objectui#6959). `mount()` settles on a
// MOCK CALL — `find('task', { $expand })` was ISSUED — and that is one
// promise resolution EARLIER than the `setLoading(false)` in `reload()`'s
// `finally`. So on return from `mount()` `ObjectGantt` may still be
// painting its `loading` branch (`Loading Gantt chart...`), and a
// SYNCHRONOUS `getByTestId` here races it. It lost that race twice in
// fifteen minutes in the merge queue — a heavier runner than PR CI —
// ejecting two PRs that do not touch `plugin-gantt` at all.
//
// `mount()`'s find-call gate STAYS: the refusal probe needs proof of the
// SCHEMA-dependent commit, and `gantt-view` alone does not carry it —
// `loading` flips false after the FIRST `find()` resolves, which happens
// before `objectSchema` lands. The two gates measure different facts, so
// this block waits for both rather than trading one for the other.
const { ds, view } = await mount(FIELD_DEFS.legacy_camel);
expect(view.getByTestId('gantt-view').getAttribute('data-count')).toBe('2');
fireEvent.click(view.getByTestId('quick-filter-trigger-project'));
const chart = await view.findByTestId('gantt-view');
expect(chart.getAttribute('data-count')).toBe('2');
fireEvent.click(await view.findByTestId('quick-filter-trigger-project'));
const panel = view.getByTestId('quick-filter-panel-project');
expect(within(panel).getByTestId('quick-filter-option-project-p1')).toBeTruthy();
expect(within(panel).queryByTestId('quick-filter-option-project-p3')).toBeNull();
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' test(plugin-gantt): read the chart through an async query, not a race by os-sam · Pull Request #6966 · objectstack-ai/objectui · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 28 additions & 0 deletions .changeset/6959-gantt-lazy-read-flake.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
---
---

Test-only: `ObjectGantt.referenceArms-6837.test.tsx` now reads the chart through an
asynchronous query instead of racing it. No published behaviour changes; no runtime code
was touched.

The block at `:257` mounted through the file's `mount()` helper, whose gate settles a MOCK
CALL — `find('task', { $expand })` was ISSUED — and then read the DOM synchronously with
`getByTestId('gantt-view')`. Those are two different facts one promise-resolution apart:
`ObjectGantt` flips `loading` false in the `finally` of `reload()`, i.e. after that find
RESOLVES, so on return from `mount()` the component can still be painting its
`Loading Gantt chart...` branch. The synchronous read raced it and lost twice in fifteen
minutes in the merge queue — a heavier environment than PR CI — ejecting two pull requests
that do not touch `plugin-gantt` at all, one of which passed and failed the queue on
byte-identical content.

The assertion is unchanged, and `mount()` keeps its find-call gate: the refusal probe above
this block needs proof of the SCHEMA-dependent commit, which the chart's presence alone does
not carry (`loading` flips false after the FIRST find resolves, before `objectSchema`
lands). The two gates measure different facts, so the block now waits for both rather than
trading one for the other.

Measured rather than assumed: with the data source's resolution delayed 500ms — the timing
FACT mutated, never the assertion — the old spelling throws
`Unable to find an element by: [data-testid="gantt-view"]` against a DOM reading exactly
`Loading Gantt chart...`, reproducing the queue failure, while the new spelling resolves
through that fallback and reads `data-count=2`.
Original file line numberDiff line numberDiff line change
Expand Up@@ -253,9 +253,25 @@ describe('ObjectGantt resolves only contract-declared target spellings (objectui
it('and degrades to the distinct loaded values rather than rendering nothing', async () => {
// Guards the refusal above against the degenerate pass: a gantt that
// rendered no quick filter at all would also never fetch `projects`.
//
// ⚠️ ASYNC reads, deliberately (objectui#6959). `mount()` settles on a
// MOCK CALL — `find('task', { $expand })` was ISSUED — and that is one
// promise resolution EARLIER than the `setLoading(false)` in `reload()`'s
// `finally`. So on return from `mount()` `ObjectGantt` may still be
// painting its `loading` branch (`Loading Gantt chart...`), and a
// SYNCHRONOUS `getByTestId` here races it. It lost that race twice in
// fifteen minutes in the merge queue — a heavier runner than PR CI —
// ejecting two PRs that do not touch `plugin-gantt` at all.
//
// `mount()`'s find-call gate STAYS: the refusal probe needs proof of the
// SCHEMA-dependent commit, and `gantt-view` alone does not carry it —
// `loading` flips false after the FIRST `find()` resolves, which happens
// before `objectSchema` lands. The two gates measure different facts, so
// this block waits for both rather than trading one for the other.
const { ds, view } = await mount(FIELD_DEFS.legacy_camel);
expect(view.getByTestId('gantt-view').getAttribute('data-count')).toBe('2');
fireEvent.click(view.getByTestId('quick-filter-trigger-project'));
const chart = await view.findByTestId('gantt-view');
expect(chart.getAttribute('data-count')).toBe('2');
fireEvent.click(await view.findByTestId('quick-filter-trigger-project'));
const panel = view.getByTestId('quick-filter-panel-project');
expect(within(panel).getByTestId('quick-filter-option-project-p1')).toBeTruthy();
expect(within(panel).queryByTestId('quick-filter-option-project-p3')).toBeNull();
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' test(plugin-gantt): read the chart through an async query, not a race by os-sam · Pull Request #6966 · objectstack-ai/objectui · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 28 additions & 0 deletions .changeset/6959-gantt-lazy-read-flake.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
---
---

Test-only: `ObjectGantt.referenceArms-6837.test.tsx` now reads the chart through an
asynchronous query instead of racing it. No published behaviour changes; no runtime code
was touched.

The block at `:257` mounted through the file's `mount()` helper, whose gate settles a MOCK
CALL — `find('task', { $expand })` was ISSUED — and then read the DOM synchronously with
`getByTestId('gantt-view')`. Those are two different facts one promise-resolution apart:
`ObjectGantt` flips `loading` false in the `finally` of `reload()`, i.e. after that find
RESOLVES, so on return from `mount()` the component can still be painting its
`Loading Gantt chart...` branch. The synchronous read raced it and lost twice in fifteen
minutes in the merge queue — a heavier environment than PR CI — ejecting two pull requests
that do not touch `plugin-gantt` at all, one of which passed and failed the queue on
byte-identical content.

The assertion is unchanged, and `mount()` keeps its find-call gate: the refusal probe above
this block needs proof of the SCHEMA-dependent commit, which the chart's presence alone does
not carry (`loading` flips false after the FIRST find resolves, before `objectSchema`
lands). The two gates measure different facts, so the block now waits for both rather than
trading one for the other.

Measured rather than assumed: with the data source's resolution delayed 500ms — the timing
FACT mutated, never the assertion — the old spelling throws
`Unable to find an element by: [data-testid="gantt-view"]` against a DOM reading exactly
`Loading Gantt chart...`, reproducing the queue failure, while the new spelling resolves
through that fallback and reads `data-count=2`.
Original file line numberDiff line numberDiff line change
Expand Up@@ -253,9 +253,25 @@ describe('ObjectGantt resolves only contract-declared target spellings (objectui
it('and degrades to the distinct loaded values rather than rendering nothing', async () => {
// Guards the refusal above against the degenerate pass: a gantt that
// rendered no quick filter at all would also never fetch `projects`.
//
// ⚠️ ASYNC reads, deliberately (objectui#6959). `mount()` settles on a
// MOCK CALL — `find('task', { $expand })` was ISSUED — and that is one
// promise resolution EARLIER than the `setLoading(false)` in `reload()`'s
// `finally`. So on return from `mount()` `ObjectGantt` may still be
// painting its `loading` branch (`Loading Gantt chart...`), and a
// SYNCHRONOUS `getByTestId` here races it. It lost that race twice in
// fifteen minutes in the merge queue — a heavier runner than PR CI —
// ejecting two PRs that do not touch `plugin-gantt` at all.
//
// `mount()`'s find-call gate STAYS: the refusal probe needs proof of the
// SCHEMA-dependent commit, and `gantt-view` alone does not carry it —
// `loading` flips false after the FIRST `find()` resolves, which happens
// before `objectSchema` lands. The two gates measure different facts, so
// this block waits for both rather than trading one for the other.
const { ds, view } = await mount(FIELD_DEFS.legacy_camel);
expect(view.getByTestId('gantt-view').getAttribute('data-count')).toBe('2');
fireEvent.click(view.getByTestId('quick-filter-trigger-project'));
const chart = await view.findByTestId('gantt-view');
expect(chart.getAttribute('data-count')).toBe('2');
fireEvent.click(await view.findByTestId('quick-filter-trigger-project'));
const panel = view.getByTestId('quick-filter-panel-project');
expect(within(panel).getByTestId('quick-filter-option-project-p1')).toBeTruthy();
expect(within(panel).queryByTestId('quick-filter-option-project-p3')).toBeNull();
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' test(plugin-gantt): read the chart through an async query, not a race by os-sam · Pull Request #6966 · objectstack-ai/objectui · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 28 additions & 0 deletions .changeset/6959-gantt-lazy-read-flake.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
---
---

Test-only: `ObjectGantt.referenceArms-6837.test.tsx` now reads the chart through an
asynchronous query instead of racing it. No published behaviour changes; no runtime code
was touched.

The block at `:257` mounted through the file's `mount()` helper, whose gate settles a MOCK
CALL — `find('task', { $expand })` was ISSUED — and then read the DOM synchronously with
`getByTestId('gantt-view')`. Those are two different facts one promise-resolution apart:
`ObjectGantt` flips `loading` false in the `finally` of `reload()`, i.e. after that find
RESOLVES, so on return from `mount()` the component can still be painting its
`Loading Gantt chart...` branch. The synchronous read raced it and lost twice in fifteen
minutes in the merge queue — a heavier environment than PR CI — ejecting two pull requests
that do not touch `plugin-gantt` at all, one of which passed and failed the queue on
byte-identical content.

The assertion is unchanged, and `mount()` keeps its find-call gate: the refusal probe above
this block needs proof of the SCHEMA-dependent commit, which the chart's presence alone does
not carry (`loading` flips false after the FIRST find resolves, before `objectSchema`
lands). The two gates measure different facts, so the block now waits for both rather than
trading one for the other.

Measured rather than assumed: with the data source's resolution delayed 500ms — the timing
FACT mutated, never the assertion — the old spelling throws
`Unable to find an element by: [data-testid="gantt-view"]` against a DOM reading exactly
`Loading Gantt chart...`, reproducing the queue failure, while the new spelling resolves
through that fallback and reads `data-count=2`.
Original file line numberDiff line numberDiff line change
Expand Up@@ -253,9 +253,25 @@ describe('ObjectGantt resolves only contract-declared target spellings (objectui
it('and degrades to the distinct loaded values rather than rendering nothing', async () => {
// Guards the refusal above against the degenerate pass: a gantt that
// rendered no quick filter at all would also never fetch `projects`.
//
// ⚠️ ASYNC reads, deliberately (objectui#6959). `mount()` settles on a
// MOCK CALL — `find('task', { $expand })` was ISSUED — and that is one
// promise resolution EARLIER than the `setLoading(false)` in `reload()`'s
// `finally`. So on return from `mount()` `ObjectGantt` may still be
// painting its `loading` branch (`Loading Gantt chart...`), and a
// SYNCHRONOUS `getByTestId` here races it. It lost that race twice in
// fifteen minutes in the merge queue — a heavier runner than PR CI —
// ejecting two PRs that do not touch `plugin-gantt` at all.
//
// `mount()`'s find-call gate STAYS: the refusal probe needs proof of the
// SCHEMA-dependent commit, and `gantt-view` alone does not carry it —
// `loading` flips false after the FIRST `find()` resolves, which happens
// before `objectSchema` lands. The two gates measure different facts, so
// this block waits for both rather than trading one for the other.
const { ds, view } = await mount(FIELD_DEFS.legacy_camel);
expect(view.getByTestId('gantt-view').getAttribute('data-count')).toBe('2');
fireEvent.click(view.getByTestId('quick-filter-trigger-project'));
const chart = await view.findByTestId('gantt-view');
expect(chart.getAttribute('data-count')).toBe('2');
fireEvent.click(await view.findByTestId('quick-filter-trigger-project'));
const panel = view.getByTestId('quick-filter-panel-project');
expect(within(panel).getByTestId('quick-filter-option-project-p1')).toBeTruthy();
expect(within(panel).queryByTestId('quick-filter-option-project-p3')).toBeNull();
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' test(plugin-gantt): read the chart through an async query, not a race by os-sam · Pull Request #6966 · objectstack-ai/objectui · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 28 additions & 0 deletions .changeset/6959-gantt-lazy-read-flake.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
---
---

Test-only: `ObjectGantt.referenceArms-6837.test.tsx` now reads the chart through an
asynchronous query instead of racing it. No published behaviour changes; no runtime code
was touched.

The block at `:257` mounted through the file's `mount()` helper, whose gate settles a MOCK
CALL — `find('task', { $expand })` was ISSUED — and then read the DOM synchronously with
`getByTestId('gantt-view')`. Those are two different facts one promise-resolution apart:
`ObjectGantt` flips `loading` false in the `finally` of `reload()`, i.e. after that find
RESOLVES, so on return from `mount()` the component can still be painting its
`Loading Gantt chart...` branch. The synchronous read raced it and lost twice in fifteen
minutes in the merge queue — a heavier environment than PR CI — ejecting two pull requests
that do not touch `plugin-gantt` at all, one of which passed and failed the queue on
byte-identical content.

The assertion is unchanged, and `mount()` keeps its find-call gate: the refusal probe above
this block needs proof of the SCHEMA-dependent commit, which the chart's presence alone does
not carry (`loading` flips false after the FIRST find resolves, before `objectSchema`
lands). The two gates measure different facts, so the block now waits for both rather than
trading one for the other.

Measured rather than assumed: with the data source's resolution delayed 500ms — the timing
FACT mutated, never the assertion — the old spelling throws
`Unable to find an element by: [data-testid="gantt-view"]` against a DOM reading exactly
`Loading Gantt chart...`, reproducing the queue failure, while the new spelling resolves
through that fallback and reads `data-count=2`.
Original file line numberDiff line numberDiff line change
Expand Up@@ -253,9 +253,25 @@ describe('ObjectGantt resolves only contract-declared target spellings (objectui
it('and degrades to the distinct loaded values rather than rendering nothing', async () => {
// Guards the refusal above against the degenerate pass: a gantt that
// rendered no quick filter at all would also never fetch `projects`.
//
// ⚠️ ASYNC reads, deliberately (objectui#6959). `mount()` settles on a
// MOCK CALL — `find('task', { $expand })` was ISSUED — and that is one
// promise resolution EARLIER than the `setLoading(false)` in `reload()`'s
// `finally`. So on return from `mount()` `ObjectGantt` may still be
// painting its `loading` branch (`Loading Gantt chart...`), and a
// SYNCHRONOUS `getByTestId` here races it. It lost that race twice in
// fifteen minutes in the merge queue — a heavier runner than PR CI —
// ejecting two PRs that do not touch `plugin-gantt` at all.
//
// `mount()`'s find-call gate STAYS: the refusal probe needs proof of the
// SCHEMA-dependent commit, and `gantt-view` alone does not carry it —
// `loading` flips false after the FIRST `find()` resolves, which happens
// before `objectSchema` lands. The two gates measure different facts, so
// this block waits for both rather than trading one for the other.
const { ds, view } = await mount(FIELD_DEFS.legacy_camel);
expect(view.getByTestId('gantt-view').getAttribute('data-count')).toBe('2');
fireEvent.click(view.getByTestId('quick-filter-trigger-project'));
const chart = await view.findByTestId('gantt-view');
expect(chart.getAttribute('data-count')).toBe('2');
fireEvent.click(await view.findByTestId('quick-filter-trigger-project'));
const panel = view.getByTestId('quick-filter-panel-project');
expect(within(panel).getByTestId('quick-filter-option-project-p1')).toBeTruthy();
expect(within(panel).queryByTestId('quick-filter-option-project-p3')).toBeNull();
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); test(plugin-gantt): read the chart through an async query, not a race by os-sam · Pull Request #6966 · objectstack-ai/objectui · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 28 additions & 0 deletions .changeset/6959-gantt-lazy-read-flake.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
---
---

Test-only: `ObjectGantt.referenceArms-6837.test.tsx` now reads the chart through an
asynchronous query instead of racing it. No published behaviour changes; no runtime code
was touched.

The block at `:257` mounted through the file's `mount()` helper, whose gate settles a MOCK
CALL — `find('task', { $expand })` was ISSUED — and then read the DOM synchronously with
`getByTestId('gantt-view')`. Those are two different facts one promise-resolution apart:
`ObjectGantt` flips `loading` false in the `finally` of `reload()`, i.e. after that find
RESOLVES, so on return from `mount()` the component can still be painting its
`Loading Gantt chart...` branch. The synchronous read raced it and lost twice in fifteen
minutes in the merge queue — a heavier environment than PR CI — ejecting two pull requests
that do not touch `plugin-gantt` at all, one of which passed and failed the queue on
byte-identical content.

The assertion is unchanged, and `mount()` keeps its find-call gate: the refusal probe above
this block needs proof of the SCHEMA-dependent commit, which the chart's presence alone does
not carry (`loading` flips false after the FIRST find resolves, before `objectSchema`
lands). The two gates measure different facts, so the block now waits for both rather than
trading one for the other.

Measured rather than assumed: with the data source's resolution delayed 500ms — the timing
FACT mutated, never the assertion — the old spelling throws
`Unable to find an element by: [data-testid="gantt-view"]` against a DOM reading exactly
`Loading Gantt chart...`, reproducing the queue failure, while the new spelling resolves
through that fallback and reads `data-count=2`.
Original file line numberDiff line numberDiff line change
Expand Up@@ -253,9 +253,25 @@ describe('ObjectGantt resolves only contract-declared target spellings (objectui
it('and degrades to the distinct loaded values rather than rendering nothing', async () => {
// Guards the refusal above against the degenerate pass: a gantt that
// rendered no quick filter at all would also never fetch `projects`.
//
// ⚠️ ASYNC reads, deliberately (objectui#6959). `mount()` settles on a
// MOCK CALL — `find('task', { $expand })` was ISSUED — and that is one
// promise resolution EARLIER than the `setLoading(false)` in `reload()`'s
// `finally`. So on return from `mount()` `ObjectGantt` may still be
// painting its `loading` branch (`Loading Gantt chart...`), and a
// SYNCHRONOUS `getByTestId` here races it. It lost that race twice in
// fifteen minutes in the merge queue — a heavier runner than PR CI —
// ejecting two PRs that do not touch `plugin-gantt` at all.
//
// `mount()`'s find-call gate STAYS: the refusal probe needs proof of the
// SCHEMA-dependent commit, and `gantt-view` alone does not carry it —
// `loading` flips false after the FIRST `find()` resolves, which happens
// before `objectSchema` lands. The two gates measure different facts, so
// this block waits for both rather than trading one for the other.
const { ds, view } = await mount(FIELD_DEFS.legacy_camel);
expect(view.getByTestId('gantt-view').getAttribute('data-count')).toBe('2');
fireEvent.click(view.getByTestId('quick-filter-trigger-project'));
const chart = await view.findByTestId('gantt-view');
expect(chart.getAttribute('data-count')).toBe('2');
fireEvent.click(await view.findByTestId('quick-filter-trigger-project'));
const panel = view.getByTestId('quick-filter-panel-project');
expect(within(panel).getByTestId('quick-filter-option-project-p1')).toBeTruthy();
expect(within(panel).queryByTestId('quick-filter-option-project-p3')).toBeNull();
Expand Down
Loading