The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →For a Power BI report embedded with the JavaScript client, wait for the SDK’s rendered event—not merely Puppeteer’s network-idle state or Power BI’s earlier loaded event. Register the listener before embedding, resolve a readiness signal when rendered fires, and reject or report failures from the SDK’s error event. Then have Puppeteer await that signal with a bounded timeout.
What “finished loading” means in Power BI
Power BI exposes two events that are easy to confuse. Microsoft defines loaded as the point when the report is initialized; it does not mean the visuals have finished drawing. The rendered event indicates that the report is fully rendered using actual data. For a test or screenshot that needs the visible report, use rendered as the completion signal. See Microsoft’s best practices for faster performance in Power BI embedded analytics and phased embedding.
There is no universal number of seconds a report needs. Microsoft notes that loading time depends on report elements, data size, and query or measure complexity. Choose a timeout that fits your report and test environment rather than treating any one duration as a guarantee.
Best option when you control the embed host: bridge the SDK event to Puppeteer
Puppeteer runs against the host page, while an embedded report may run in a cross-origin iframe. Instead of trying to inspect Power BI’s internal DOM from Puppeteer, let the host page listen to the SDK event and publish a stable, application-owned readiness signal. Register both listeners immediately after embedding, before code that waits for the result.
#1 Best Overall
Host-page event bridge
// Run in the host application when creating the report.
const report = powerbi.embed(
document.querySelector('#report'),
embedConfig
);
window.__powerBiReady = false;
window.__powerBiError = null;
report.on('error', event => {
window.__powerBiError = event.detail;
});
report.on('rendered', () => {
window.__powerBiReady = true;
});
This readiness flag is intentionally owned by your application. The error is retained separately so the test can distinguish an SDK failure from a wait that simply timed out.
Puppeteer test
const page = await browser.newPage();
await page.goto(reportUrl, {
waitUntil: 'domcontentloaded',
timeout: 60000,
});
await page.waitForFunction(
() => window.__powerBiReady === true || window.__powerBiError !== null,
{ timeout: 60000 }
);
const error = await page.evaluate(() => window.__powerBiError);
if (error) {
throw new Error(`Power BI report failed: ${JSON.stringify(error)}`);
}
// The report's initial rendered event has fired; capture or continue the test.
await page.screenshot({ path: 'report.png', fullPage: true });
The example’s 60-second limits are illustrative bounds, not a claim that reports normally finish in that time. Set navigation and report-readiness timeouts separately if your host page and report have different expected behavior. Puppeteer’s waitForFunction API waits for a page condition to become true and accepts a timeout.
Promise-based readiness
If application code needs to await readiness too, create a Promise before the embedding action and settle it from the SDK events. This avoids a race in which a fast report renders before the listener is attached.
let readyResolve;
let readyReject;
window.powerBiReady = new Promise((resolve, reject) => {
readyResolve = resolve;
readyReject = reject;
});
const report = powerbi.embed(embedContainer, embedConfig);
report.on('error', event => {
readyReject(event.detail);
});
report.on('rendered', () => {
window.__powerBiReady = true;
readyResolve();
});
In production code, also initialize window.__powerBiReady to false and window.__powerBiError to null before calling powerbi.embed if Puppeteer will observe those values. If the Promise rejects, catch the rejection in application code or use the error marker pattern above so the page does not produce an unhandled rejection.
Phased embedding: wait for the render phase, not just load
With phased embedding, the host controls initialization and rendering separately. Call powerbi.load, wait for loaded when initialization is complete, perform any required setup, then call report.render. Resolve the Puppeteer readiness signal only from the subsequent rendered event. Microsoft documents that rendered fires when the report finishes rendering in its phased embedding guidance.
const report = powerbi.load(embedContainer, embedConfig);
report.on('error', event => {
window.__powerBiError = event.detail;
});
report.on('loaded', async () => {
// Apply any setup that must happen before rendering.
report.on('rendered', () => {
window.__powerBiReady = true;
});
await report.render();
});
Attach the rendered listener before calling render(). Otherwise, a quick render could complete before the listener is in place. Adapt setup and error handling to the lifecycle of your application rather than relying on internal report elements.
Detecting a later render after filters or navigation
The initial render is not the only time the report can emit rendered. It can fire again after interactions such as applying filters. A readiness flag that remains true after the first render cannot prove that a later action has completed. For interaction tests, create a fresh per-action Promise or reset a generation counter before triggering the action, then wait for the next render event.
function waitForNextRender(report, timeoutMs = 60000) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
report.off('rendered', onRendered);
report.off('error', onError);
reject(new Error(`No rendered event within ${timeoutMs} ms`));
}, timeoutMs);
function cleanup() {
clearTimeout(timer);
report.off('rendered', onRendered);
report.off('error', onError);
}
function onRendered() {
cleanup();
resolve();
}
function onError(event) {
cleanup();
reject(new Error(JSON.stringify(event.detail)));
}
report.on('rendered', onRendered);
report.on('error', onError);
});
}
const nextRender = waitForNextRender(report);
await report.setFilters(filters);
await nextRender;
Register the per-action wait before invoking the action that should cause a render. Confirm that the action actually triggers the report update you intend to test; a filter operation that does not change the report may not produce the event your test expects.
Rank #3
When you cannot change the host page
If the page does not expose an SDK event or a readiness marker, Puppeteer cannot reliably infer full visual completion from the outer document alone. Use fallback checks in descending order of reliability, and label the result as a heuristic when it is based on visual or network activity.
- Wait for an application-owned signal. Prefer a stable container attribute or marker maintained by the host. For example:
await page.waitForSelector('#report[data-report-ready="true"]', { timeout: 60000 }); - Use a visible loading indicator only as a heuristic. If the application has a loading logo or progress element, wait for it to become hidden. Its disappearance may indicate that a UI transition completed, but it is not as authoritative as the SDK’s
renderedevent. - Use network idle only as a stabilization aid. After the application signal or heuristic, optionally wait for network activity to settle. Puppeteer’s waitForNetworkIdle API waits for network idleness and always waits at least the configured idle time. It does not establish that Power BI visuals are complete.
A simple marker bridge, when you can make a small host-page change, looks like this:
// Host page
const report = powerbi.embed(document.querySelector('#report'), config);
report.on('error', event => {
window.__powerBiError = event.detail;
});
report.on('rendered', () => {
document.querySelector('#report').setAttribute('data-report-ready', 'true');
});
// Puppeteer test
await page.goto(reportUrl, { waitUntil: 'domcontentloaded' });
await page.waitForSelector('#report[data-report-ready="true"]', {
timeout: 60000,
});
const error = await page.evaluate(() => window.__powerBiError || null);
if (error) throw new Error(JSON.stringify(error));
Why common Puppeteer waits give false confidence
networkidle0 on navigation
page.goto(url, {waitUntil: 'networkidle0'}) observes the page’s network activity; it does not certify that an embedded, cross-origin Power BI report has finished drawing its visuals. Work in an iframe, cached data, or later requests can make network idleness diverge from visual readiness. Use navigation completion to know the host document is available, then wait for the report-specific signal.
The loaded event
loaded marks initialization, not the completion of visual rendering. It is useful in phased embedding when you need to configure the report before calling render(); it is not the right final signal for a screenshot that must contain rendered visuals.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Repair Windows errors before they cause bigger problems3Scan for outdated or missing drivers - takes under a minuteRank #4
Fixed sleeps
A fixed delay is wasteful on a fast run and unreliable on a slow or data-heavy one. Replace setTimeout-style waiting with an event or marker and a finite timeout. A timeout should fail with useful diagnostics, not silently proceed to capture an incomplete report.
Power BI internal DOM selectors
Selectors aimed at undocumented internal markup couple the test to implementation details that can change. Prefer the SDK event or a marker your application owns and can keep stable through UI redesigns.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Timeout diagnostics and failure handling
When the readiness wait expires, collect evidence that distinguishes a delayed report from a navigation, embed, or test failure. Include the SDK error detail whenever available.
- Record the page URL and whether navigation completed.
- Capture a screenshot of the current state and the browser console messages.
- Record iframe URLs and failed network requests; this can reveal a failed embed or blocked resource without requiring Puppeteer to inspect a cross-origin frame’s internals.
- Check whether the application-owned marker was initialized and whether the host registered the event listeners before embedding.
- Report the configured timeout and the report or interaction being tested.
Use separate, finite bounds for navigation and report readiness. Reports vary with their contents and data path, so tune the readiness limit from your own environment and test cases; do not infer a universal completion time from a single run.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Best Value
Or skip the browser setup
If your goal is a screenshot file rather than testing Power BI’s SDK lifecycle, ScreenshotNeo can return a screenshot or PDF from one GET request. It is a website screenshot API and MCP server from Yorker Media. For example, the cURL call below captures a public page; replace the target URL with the page you need. See the ScreenshotNeo documentation for request options and response details.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
ScreenshotNeo accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each of those steps can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots a month without a card; paid plans start at $5 for 3,000 shots. It does not replace an SDK-event test when you need to assert that Power BI itself rendered successfully.
Sign up for ScreenshotNeo’s free plan: 1,000 screenshots a month, no card required.
Frequently Asked Questions
Can Puppeteer listen directly for Power BI’s rendered event?
Not by itself when the event is emitted by client code inside a cross-origin embedded report. Have the host application bridge the SDK event to a marker or Promise that Puppeteer can observe.
Recommended Free Tools
Does rendered fire only once?
No. It can fire again after filters or other interactions, so use a new wait tied to each action you are testing.
What should I capture when the wait times out?
Capture the current screenshot, console messages, iframe URLs, failed requests, and any Power BI error detail exposed by the host.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




