
package com.smartgwt.client.docs;

/**
 * <h3>Integrating Smart GWT with Playwright</h3>
 * Playwright is a modern automated testing platform that can be used to test web applications.
 *  Smart GWT supports a number of features to easily integrate with Playwright, making
 *  it extremely straightforward to start creating effective automated tests for
 *  Smart GWT applications.
 *  <p>
 * <i>For an overview of Automated Testing in Smart GWT, see the documentation {@link
 * com.smartgwt.client.docs.AutomatedTesting here}.</i>
 *  <p>
 * The Playwright website contains very helpful guides to walk you through how to install
 * Playwright,
 *  how to run Playwright tests, and how to create test suites. If you're new to Playwright,
 *  we'd recommend you start 
 *  <a href='https://playwright.dev/docs/intro' target='_blank'>here</a>.
 *  You should be able to rapidly learn how to install Playwright and how to create 
 *  and run e2e (end-to-end) tests.
 *  <p>  
 *  <b>Custom Playwright commands for Smart GWT applications</b>  
 *  <p>
 *  The Smart GWT SDK ships with a sample <code>commands.js</code> file, available under:
 *  <pre>
 *  commands.js</pre>
 *  This file contains custom helper methods which simplify interacting with a running
 *  Smart GWT application as Playwright runs your tests. See below for more details.
 *  <P>
 *  <b>Using <i>locators</i> to interact with Smart GWT User Interface components</b>  
 *  <p>
 * The {@link com.smartgwt.client.docs.AutoTestLocator} subsystem is used to reliably identify DOM
 * elements
 *  generated within a Smart GWT application.
 *  <p>
 * For general concepts, see {@link com.smartgwt.client.docs.ObtainingLocators}. The following is
 * Playwright-specific guidance.  
 *  <p>
 *  The sample <code>commands.js</code> file includes a <code>getSC()</code> method that uses 
 * {@link com.smartgwt.client.util.AutoTest#waitForElement AutoTest.waitForElement()} to wait for
 * any pending system actions, resolve an AutoTestLocator 
 * to a DOM element, and yield it back as a Playwright ElementHandle. You can make use of this
 * method in your test code as follows:
 *  <pre>
 *  const element = await page.getSC(locator);
 *  await element.click();
 *  </pre>
 *  or directly:
 *  <pre>
 *  await page.clickSC(locator);
 *  </pre>
 * <code>clickSC()</code> calls <code>getSC()</code> and handles the click interaction in just one
 * call.
 *  <p>
 *  <b>Scroll behavior in Playwright</b>  
 *  <p>
 * For general concepts, see {@link com.smartgwt.client.docs.ScrollingBehavior}. The following is
 * Playwright-specific guidance.  
 *  <p>
 * Playwright actions like <a
 * href='https://playwright.dev/docs/api/class-elementhandle#element-handle-click'
 * target='_blank'>click</a> may scroll the 
 * target element into view. If Smart GWT components redraw synchronously on scroll, this can
 * interfere 
 *  with Playwright actions. To avoid issues, ensure the element is in view before interacting.
 *  <P>
 * For Smart GWT skins that use {@link com.smartgwt.client.widgets.Canvas#getShowCustomScrollbars
 * custom scrollbars}, the Playwright
 * <a href='https://playwright.dev/docs/api/class-page#page-mouse-wheel'
 * target='_blank'>mouse.wheel</a> method may not see the component
 * as a valid target for scrolling. For this reason the sample <code>commands.js</code> file
 * includes
 * a <code>scrollSC()</code> method. This method takes a locator plus target left and top position
 *  as arguments. Note that you can specify left and top as an explicit pixel position, or use
 * a percentage string like "50%". For example, to scroll some component to its mid point
 * vertically
 *  while leaving the horizontal scroll position unchanged you could invoke:
 *  <pre>
 *  await page.scrollSC(locator, null, "50%");
 *  </pre>
 *  <P>
 *  <b>Interacting with FormItems in Playwright</b>  
 *  <p>
 * Smart GWT's SelectItem, ComboBoxItem and CheckboxItem are based on custom HTML rather than
 * built-in 
 * browser controls, because this is required to provide advanced functionality such as
 * multi-column dropdowns.  
 *  Because of this, the <i>check()</i> and <i>uncheck()</i> functions for CheckboxItem
 *  and the <i>select()</i> function for SelectItem and ComboBoxItem are not applicable.
 *  <p>
 *  Instead, the "click()" function is the way to interact with these controls, for example:
 *  <pre>
 *       await page.goto('https://smartclient.com/smartclient-latest/showcase/?id=updateOperation');
 * 
 *       // Click to start editing the grid
 *       await page.clickSC('//testRoot[]/child[index=0||length=1||Class=VStack||classIndex=0||classLength=1]' +
 *               '/member[index=1||length=4||Class=ListGrid||classIndex=0||classLength=1]/body/row[1]/col[2]');
 * 
 *       // Type a value into the 'description' item
 *       await page.typeSC('//testRoot[]/child[index=0||length=1||Class=VStack||classIndex=0||classLength=1]/' +
 *               'member[index=2||length=4||Class=DynamicForm||classIndex=0||classLength=1]/item[name=description]/element',
 *               'Glue Pelikan Roll-fix Refill Permanent #955');
 * 
 *       // Click the 'units' item to show the pickList
 *       await page.clickSC('//testRoot[]/child[index=0||length=1||Class=VStack||classIndex=0||classLength=1]/' +
 *               'member[index=2||length=4||Class=DynamicForm||classIndex=0||classLength=1]/item[name=units]/textbox');
 * 
 *       // Click a value in the pickList drop down
 *       await page.clickSC('//testRoot[]/child[index=0||length=1||Class=VStack||classIndex=0||classLength=1]/' + 
 *               'member[index=2||length=4||Class=DynamicForm||classIndex=0||classLength=1]/item[name=units]/pickList/body/row[3]/col[0]');
 * 
 *       // Click to toggle the value of the 'inStock' checkbox'
 *       await page.clickSC('//testRoot[]/child[index=0||length=1||Class=VStack||classIndex=0||classLength=1]/' +
 *               'member[index=2||length=4||Class=DynamicForm||classIndex=0||classLength=1]/item[name=inStock]/valueicon');
 * 
 *       // Click the Save button
 *       await page.clickSC('//testRoot[]/child[index=0||length=1||Class=VStack||classIndex=0||classLength=1]/' +
 *               'member[title=Save]/');
 * 
 *       // Verify the value has been updated
 *       const element = await page.getSC('//testRoot[]/child[index=0||length=1||Class=VStack||classIndex=0||classLength=1]/' +
 *               'member[index=1||length=4||Class=ListGrid||classIndex=0||classLength=1]/body/row[1]/col[2]');
 *       const text = await element.textContent();
 *       expect(text.trim()).toBe('Glue Pelikan Roll-fix Refill Permanent #955');
 *  </pre>
 * <b>Using force-click to dismiss {@link com.smartgwt.client.widgets.Canvas#showClickMask click
 * masks}</b>
 *  <P>
 *  In some cases you may need to dismiss a Smart GWT click-mask by clicking.
 * Examples include certain styles of drop-down, grid editing interactions, etc. - that are
 * dismissed 
 *  via an outside-click. Playwright will reject attempts to send a <code>click()</code> to some 
 * element under the click-mask by default as the target element will be obscured by the click
 * mask
 * in the DOM, and so is not directly "visible" as far as Playwright is aware. You can handle this
 * by
 * specifying <a href='https://playwright.dev/docs/api/class-elementhandle#element-handle-click'
 * target='_blank'>force:true</a>
 *  on your click method.
 *  <P>
 *  For example if you have a <i>multiple:true</i> SelectItem, a clickMask is used to watch
 *  for the user clicking outside the SelectItem drop-down.
 *  To dismiss the drop-down from a Playwright test,
 *  we recommend sending a <code>{force: true}</code> click() 
 *  method to some other element on the page:
 *  <pre>
 *      await page.getSC(locator).click({force: true}) / await page.clickSC(locator, {force: true})
 *  </pre>
 *  In this case <i>locator</i> refers to the target component that we click on the app to 
 *  dismiss the dropdown. Note that depending on how the application is configured, this click may
 *  dismiss the dropdown and prevent the click action from firing for the target that was actually
 *  clicked. You can handle this by invoking a second click to mimic 
 *  the user interaction. For example:
 *  <pre>
 *      await page.getSC(locator).click({force: true});
 *      await page.getSC(locator).click();
 *  </pre>
 *  <P>
 *  
 *  <b>Waiting for asynchronous actions in Playwright</b>  
 *  <p>
 * Because the <code>getSC()</code> method uses {@link
 * com.smartgwt.client.util.AutoTest#waitForElement AutoTest.waitForElement()}, which will not
 * resolve
 * until all {@link com.smartgwt.client.util.AutoTest#isSystemDone outstanding asynchronous system
 * actions have completed}, it is
 *  not usually necessary to write test code that explicitly waits for actions to complete 
 *  (using <code>page.waitForTimeout()</code> calls, for example).<br>
 *  In other words: if an action in a test kicks off a Smart GWT DataSource operation
 * and the next action is using <code>getSC()</code> to interact with another Smart GWT component,
 * 
 * the test will automatically wait for the asynchronous operation from the first action to
 * complete 
 *  before proceeding with the second action.
 *  <P>
 *  This alone may not be sufficient to handle every asynchronous behavior in an application.
 * In some cases you may want to explicitly wait for the Smart GWT framework to complete some
 * action 
 * without having a subsequent <code>getSC()</code> call in your test. The {@link
 * com.smartgwt.client.util.AutoTest#waitForSystemDone AutoTest.waitForSystemDone()}
 *  method can be used to handle this. The <code>commands.js</code> sample file includes 
 *  a custom method <code>"waitForSCDone"</code> which wraps this method in a Playwright helper.
 *  <P>
 *  To explicitly wait for all asynchronous Smart GWT actions to complete, call the method in your
 *  test code as follows:
 *  <pre>
 *   await page.waitForSCDone();
 *  </pre>
 * For more details on explicitly handling asynchronous actions without using
 * <code>getSC()</code>, 
 *  see the {@link com.smartgwt.client.docs.WaitingForAsyncActions} section.
 *  <P>
 * Additionally you may have asynchronous behaviors that are unrelated to Smart GWT interactions,
 * such
 * as network activity that does not go through the Smart GWT {@link
 * com.smartgwt.client.rpc.RPCManager}, asynchronous rendering
 * of third party widgets, etc. In these cases {@link
 * com.smartgwt.client.util.AutoTest#isSystemDone AutoTest.isSystemDone()} may return true even
 * though
 *  the application is not ready for further input.
 *  <P>
 *  The <code>options</code> parameter for <code>getSC()</code> can be used to 
 * change the {@link com.smartgwt.client.util.ElementWaitConfig#getWaitStyle waitStyle} passed to
 * {@link com.smartgwt.client.util.AutoTest#waitForElement AutoTest.waitForElement()}.
 *  If you request <code>"element"</code> rather than <code>"system"</code>, instead of relying on
 * {@link com.smartgwt.client.util.AutoTest#isSystemDone AutoTest.isSystemDone()}, the framework
 * will continue trying to resolve the locator to
 *  an element until the command times out. This gives you an easy way to instruct the test case
 * to keep trying to resolve a locator even after {@link
 * com.smartgwt.client.util.AutoTest#isSystemDone AutoTest.isSystemDone()} returns true.
 *  <P>
 *  Both <code>waitForSCDone()</code> and <code>getSC()</code> support being passed an explicit
 *  timeout on the <code>options</code> parameter. This governs how long the methods
 *  will wait for system quiescence / for the locator to be resolved. If no explicit
 *  timeout was specified, the default wait time for these methods is 30 seconds,
 * but this can be customized by setting <code>"scCommandTimeout"</code> in your
 * SmartClientCommands configuration.
 *  <P>
 * Note that as long as <code>waitStyle</code> is set to <code>"system"</code>, it is very rare
 * for
 *  <code>getSC()</code> methods to time out as the application will wait for
 * {@link com.smartgwt.client.util.AutoTest#isSystemDone AutoTest.isSystemDone()} and then attempt
 * to resolve the locator. If it fails to
 *  resolve the locator to an element it will return <code>null</code> immediately rather than
 *  continue attempting to resolve the locator until the command times out.
 *  <P>
 *  If <code>getSC()</code> or <code>waitForSCDone()</code> does time out the Playwright test
 *  will fail.
 *  <P>
 *  <b>RPC timing logs in Playwright</b>  
 *  <p>
 * For general concepts, see {@link com.smartgwt.client.docs.UsingRPCTimingLogs}. The following is
 * Playwright-specific guidance.  
 *  <p>
 *  The sample <code>commands.js</code> file includes a method <code>"enableSC_RPCTimeout"</code>
 * which makes use of console logging in Playwright to take advantage of these APIs and log timing
 * information for slow requests.
 *  <P>
 *  <b>Utility functions</b>
 *  <p>
 *  The <code>commands.js</code> file also exports standalone utility functions:
 *  <ul>
 * <li><code>scGoto(page, url, loopDetectSeconds?)</code> - Navigates to URL and waits for Smart
 * GWT page load
 * (isc.Page.isLoaded()) and AutoTest.waitForSystemDone(). Optionally enables infinite loop
 * detection during page load.</li>
 * <li><code>detectJSLoops(page, timeoutSeconds)</code> - Returns <code>{monitor, stop}</code>
 * where <code>monitor</code>
 * is a Promise that rejects if the browser becomes unresponsive for the specified number of
 * seconds. Detects
 * <i>continuous</i> JS execution (infinite loops), not total elapsed time. Tests must race the
 * monitor promise against
 *  their operations using <code>Promise.race([monitor, testOperations()])</code>.</li>
 *  <li><code>captureISCLogs(page, outputPath)</code> - Captures Smart GWT diagnostic logs from
 * <code>isc.Log.getMessages()</code> and writes them to a file, useful for debugging test
 * failures.</li>
 *  </ul>
 *  <P>
 *  <b>Detecting infinite loops with detectJSLoops()</b>
 *  <p>
 * The <code>detectJSLoops()</code> utility detects when the browser JavaScript engine becomes
 * unresponsive
 * due to infinite loops or other blocking code. Unlike standard timeouts which measure total
 * elapsed time,
 * <code>detectJSLoops()</code> monitors browser responsiveness - a test can run for hours without
 * triggering
 *  the detector as long as the browser remains responsive.
 *  <p>
 * The monitor works by repeatedly attempting <code>page.evaluate()</code> calls from Node.js
 * every 2 seconds.
 * If any evaluation doesn't return within the specified timeout, the browser is hung and the test
 * fails.
 *  <p>
 *  <b>Style 1: Background monitoring (recommended):</b>
 *  <pre>
 *  const { detectJSLoops } = require('path/to/commands');
 * 
 *  test('my test with hang detection', async ({ page }) => {
 *    const stopMonitor = await detectJSLoops(page, 20);
 * 
 *    try {
 *      await page.goto('/myapp.html');
 *      await page.clickSC('//Button[ID="submit"]');
 *      await page.waitForSCDone();  // IMPORTANT: wait for async operations
 *    } finally {
 *      await stopMonitor();  // Always cleanup
 *    }
 *  });
 *  </pre>
 *  <p>
 *  <b>Style 2: Explicit Promise.race (for advanced control):</b>
 *  <pre>
 *  const { detectJSLoops } = require('path/to/commands');
 * 
 *  test('my test with explicit race', async ({ page }) => {
 *    const { monitor, stop } = await detectJSLoops(page, 20);
 * 
 *    try {
 *      await Promise.race([
 *        monitor,  // Explicit - clearer error propagation
 *        (async () => {
 *          await page.goto('/myapp.html');
 *          await page.clickSC('//Button[ID="submit"]');
 *          await page.waitForSCDone();
 *        })()
 *      ]);
 *    } finally {
 *      await stop();
 *    }
 *  });
 *  </pre>
 *  <p>
 *  <b>Using scGoto() with automatic page load protection:</b>
 *  <pre>
 *  const { scGoto } = require('path/to/commands');
 * 
 *  // Default - automatically monitors for hangs during page load (25s timeout)
 *  await scGoto(page, '/myapp.html');
 * 
 *  // Custom timeout for page load hang detection
 *  await scGoto(page, '/myapp.html', 10);
 * 
 *  // Disable hang detection during page load
 *  await scGoto(page, '/myapp.html', 0);
 * 
 *  // For hang detection during test operations, use detectJSLoops separately:
 *  await scGoto(page, '/myapp.html');  // Page load protected
 *  const stopMonitor = await detectJSLoops(page, 20);
 *  try {
 *    await page.clickSC('//Button[ID="save"]');
 *    await page.waitForSCDone();
 *  } finally {
 *    await stopMonitor();
 *  }
 *  </pre>
 *  <p>
 * <b>IMPORTANT - Always wait for async operations:</b> End all tests with
 * <code>waitForSCDone()</code>
 *  or <code>waitForSystemDone()</code>. If your test code completes but async operations continue
 *  (like redraws or server calls) and hit an infinite loop, the monitor will have already stopped
 * and won't detect the hang. The test will appear to pass when it shouldn't. There's also a small
 * race window between test completion and <code>stop()</code> being called where hangs won't be
 * detected.
 *  <P>
 * <code>enableSC_RPCTimeout</code> should typically be called only once, at the beginning of your
 * test. It will remain active
 *  as long as the page is loaded. The method takes the following arguments:
 * <ul><li><code>logThreshold</code> Any RPCs whose duration exceeds this threshold will log
 * timing information
 *          via a console.log() call.</li>
 * <li><code>timeoutThreshold</code> Any RPCs whose duration exceeds this threshold will cause the
 * test to fail.</li>
 * <li><code>options</code> This parameter in an object where you can set various attributes to
 * configure 
 *          logging behavior. These include:
 *           <ul>
 *               <li><code>logDetail</code>: one of <code>"none"</code>, <code>"summary"</code>, 
 *                   <code>"detailed"</code> or <code>"all"</code></li>
 *    <li><code>logSuccess</code>: If true, log a 'success' type notification for RPC transactions
 *               that do not exceed the specified timing threshold. This log will not include any 
 *                   explicit timing data</li>
 * <li><code>includeClientTimings</code>: Should detailed timing for client-processing be
 * included?</li>
 * <li><code>includeServerTimings</code>: Should detailed timing for server-processing be
 * included?</li>
 *           </ul>
 *      </li>
 *  </ul>
 *  See the implementation in <code>commands.js</code> for more details.
 *  <P>
 *  Here's an example of how this might be used in a test file:
 *  <pre>
 *  // If the turnaround takes more than this many millis, log the timing information
 *  const LOG_TIMEOUT = 1000;
 *  // If the turnaround takes longer than this many millis, the test fails
 *  const FAILURE_TIMEOUT = 5000;
 *  
 *  test('Test Suite', async ({ page }) => { 
 *      await page.goto(targetUrl); 
 *      await page.enableSC_RPCTimeout(LOG_TIMEOUT, FAILURE_TIMEOUT, {logDetail:"detailed"});
 *      
 *      // test code goes here
 *  });
 *  </pre>
 * Note: <code>enableSC_RPCTimeout()</code> is effectively "invisible" to the flow of your test
 * unless 
 *  a transaction is encountered which exceeds the timeoutThreshold. If you want to 
 * explicitly wait for all pending actions to complete at any point, including waiting for active
 * RPCRequests
 *  to be resolved, you can use the <code>waitForSCDone</code> method described above.
 *  <P>
 *  <b>Drag and drop in Playwright</b>  
 *  <p>
 *  Playwright provides built-in support for drag and drop operations through the
 * <a href='https://playwright.dev/docs/api/class-page#page-drag-and-drop'
 * target='_blank'>dragAndDrop</a> method.
 *  The sample <code>commands.js</code> file includes a <code>dragAndDropSC()</code> method that
 *  handles complex drag and drop operations in Smart GWT grids and lists, including
 *  logic for empty grids, row positioning, and drop position adjustments.
 *  <P>
 *  Here's an example of how to use drag and drop in Playwright:
 *  <pre>
 *      await page.dragAndDropSC(sourceLocator, targetLocator);
 *  </pre>
 * where <i>sourceLocator</i> and <i>targetLocator</i> are AutoTest locators for the source and
 * target elements.
 *  <P>
 *  <b>Final Note</b>
 *  <P>
 *  In addition to the custom methods and approaches described above, 
 *  developers can also always use the
 * <a href='https://playwright.dev/docs/api/class-page#page-evaluate'
 * target='_blank'>page.evaluate()</a> method 
 *  to execute arbitrary JavaScript with access to the application scope.
 *  <P>
 *  <b>Playwright configuration for custom Smart GWT commands</b>
 *  <P>
 *  The custom methods shipped in the Smart GWT SDK will respect the following 
 *  settings if present in the SmartClientCommands configuration:
 *  <ul>
 *  <li><i>scLogCommands</i>: Boolean - if true each command will be logged via console.log()</li>
 * <li><i>scCommandTimeout</i>: Number - default timeout for getSC() and waitForSCDone() in
 * ms</li>
 *  </ul>
 *  <P>
 * @see com.smartgwt.client.tools.stream.EventStream#getPlaywrightEventScript
 * @see com.smartgwt.client.tools.stream.EventStream#getPlaywrightScriptFromData
 * @see com.smartgwt.client.tools.stream.EventStream#getPlaywrightScript
 */
public interface SmartClientPlaywright {
}
