
package com.smartgwt.client.docs;

/**
 * <h3>Integrating Smart GWT with Cypress</h3>
 * Cypress is an automated testing platform that can be used to test web applications.
 *  Smart GWT supports a number of features to easily integrate with Cypress, 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 Cypress website contains very helpful guides to walk you through how to install Cypress,
 *  how to run the Cypress application, and how to create tests. If you're new to Cypress,
 *  we'd recommend you start 
 * <a href='https://docs.cypress.io/guides/getting-started/installing-cypress'
 * target='_blank'>here</a>.
 *  You should be able to rapidly learn how to install cypress and how to create 
 *  and run e2e (end-to-end) tests.
 *  <p>
 *  <b>Custom cypress commands for Smart GWT Applications</b>
 *  <P>
 *  The Smart GWT SDK ships with a sample <code>commands.js</code> file, available under
 *  <code>
 *        commands.js</code>.
 *  <P>
 *  This file contains some 
 * <a href='https://docs.cypress.io/api/cypress-api/custom-commands' target='_blank'>custom
 * commands</a>
 *  which simplify interacting with a running
 *  Smart GWT application as cypress 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>
 *  You have two options for obtaining locators from your app:
 *  <p>
 *  <b>1.-</b> The Developer Console.
 *  <p>
 *  Open the Developer Console and, in the first tab titled "Results", click on the link near 
 *  the middle of the page, titled "Show AutoTest Locators." Now as you click on elements
 * within the application, the locator for the element will be displayed in the Developer Console.
 *  Double-click the locator (or select and use <code>Ctrl+C</code>) to copy it to the clipboard.
 *  <p>
 * <b>2.-</b> Via the {@link com.smartgwt.client.util.AutoTest#installLocatorShortcut
 * AutoTest.installLocatorShortcut()} script. After running the
 *  scriptlet to install the locator shortcut, you can simply click on the target element
 *  while holding down the appropriate modifier keys and the appropriate locator string
 *  will be copied to the clipboard.
 *  <P>
 *  <b>Resolving locators back to DOM elements in Cypress</b>
 *  <P>
 * A stored locator string can be resolved back to a DOM element via {@link
 * com.smartgwt.client.util.AutoTest#getElement AutoTest.getElement()}
 * or {@link com.smartgwt.client.util.AutoTest#waitForElement AutoTest.waitForElement()}. Using
 * <code>waitForElement()</code> is preferable 
 *  as this will not resolve until any active, asynchronous operations within an application
 *  have completed.
 *  <P>
 *  The sample <code>commands.js</code> file includes a command <code>"getSC"</code> which uses
 *  <code>waitForElement()</code> to wait for any pending system actions, then
 *  resolve an AutoTestLocator to its target DOM element and yield it back.
 *  You can make use of this command in your test code as follows:
 *  <pre>
 *   cy.getSC(&lt;locator&gt;).click();
 *  </pre>
 *  <p>
 *  <b>Cypress actions native scroll behavior</b>
 *  <P>
 * By default Cypress actions such as <a href='https://docs.cypress.io/api/commands/click'
 * target='_blank'>click</a>
 *  can cause the target element to be scrolled on the page. See the <code>scrollBehavior</code>
 *  option described 
 * <a href='https://docs.cypress.io/guides/references/configuration#Actionability'
 * target='_blank'>here</a>.
 *  <P>
 *  In some cases Smart GWT components may redraw synchronously on scroll which may
 *  interfere with your Cypress test's execution.
 * For example if a listGrid is {@link com.smartgwt.client.widgets.grid.ListGrid#getShowAllRecords
 * incrementally rendering} its
 *  records, a scroll may require a redraw to ensure the user doesn't see unrendered cells that
 *  were previously outside the viewport. If this happens while a Cypress click action is in 
 *  progress, Cypress will throw an error as the target element will have been replaced 
 *  in the DOM while the action was still in progress. Developers may avoid this problem 
 *  by ensuring the target element is already scrolled into view, and specifying
 *  <code>scrollBehavior:false</code> on Cypress' click command options.
 *  <p>
 *  <P>
 *  <b>Scrolling Smart GWT components</b>
 *  <P>
 *  In some cases a Cypress test may need to explicitly change the scroll position of
 *  a Smart GWT component.
 *  <P>
 * For Smart GWT skins that use {@link com.smartgwt.client.widgets.Canvas#getShowCustomScrollbars
 * custom scrollbars}, the Cypress
 * <a href='https://docs.cypress.io/api/commands/scrollto' target='_blank'>scrollTo</a> command
 * 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> command. This command 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>
 *   cy.scrollSC(&lt;locator&gt;, null, "50%");
 *  </pre>
 *  <b>Interacting with Smart GWT FormItems</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>
 *       cy.visit('https://smartclient.com/smartclient-latest/showcase/?id=updateOperation')
 * 
 *       // Click to start editing the grid
 *       cy.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]'
 *       ).click()
 * 
 *       // Type a value into the 'description' item
 *       cy.getSC('//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'
 *       ).type('Glue Pelikan Roll-fix Refill Permanent #955')
 * 
 *       // Click the 'units' item to show the pickList
 *       cy.getSC('//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()
 * 
 *       // Click a value in the pickList drop down
 *       cy.getSC('//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()
 * 
 *       // Click to toggle the value of the 'inStock' checkbox'
 *       cy.getSC('//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()
 * 
 *       // Click the Save button
 *       cy.getSC('//testRoot[]/child[index=0||length=1||Class=VStack||classIndex=0||classLength=1]/' +
 *               member[title=Save]/'
 *       ).click()
 * 
 *       // Verify the value has been updated
 *       cy.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]'
 *       ).invoke('text').then((text) =&gt; { 
 *           expect(text.trim()).to.equal('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. Cypress 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 Cypress is aware. You can handle this by
 * specifying <a href='https://docs.cypress.io/api/commands/hover#Force-click'
 * target='_blank'>&#123;force:true&#125;</a>
 *  on your click command.
 *  <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 Cypress test,
 *  we recommend sending a <code>{force: true}</code> click() 
 *  command to some other element on the page:
 *  <pre>
 *      cy.getSC(&lt;locator&gt;).click({force: true})
 *  </pre>
 * In this case <i>&lt;locator&gt;</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>
 *      cy.getSC(&lt;locator&gt;).click({force: true}).click()
 *  </pre>
 *  <P>
 *  <b>Waiting for Asynchronous application actions</b>
 *  <P>
 * Because the <code>getSC()</code> command 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>cy.wait()</code> calls, for example).<br>
 *  To put it another way: 
 *  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 command <code>"waitForSCDone"</code> which wraps this method in a Cypress command.
 *  <P>
 *  To explicitly wait for all asynchronous Smart GWT actions to complete, call the method in your
 *  test code as follows:
 *  <pre>
 *   cy.waitForSCDone();
 *  </pre>
 * 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 commands
 *  will wait for system quiescense / for the locator to be resolved. If no explicit
 *  timeout was specified, the default wait time for these commands is 30 seconds,
 *  but this can be customized by setting <code>"scCommandTimeout"</code> in your Cypress config.
 *  <P>
 * Note that as long as <code>waitStyle</code> is set to <code>"system"</code>, it is very rare
 * for
 *  <code>getSC()</code> commands 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 Cypress test
 *  will fail.
 *  <P>
 * <b>Logging Timing information for {@link com.smartgwt.client.rpc.RPCRequest RPC} and {@link
 * com.smartgwt.client.data.DSRequest DataSource} transactions</b>
 *  <P>
 * Smart GWT provides a number of helpful APIs which allow developers to intercept slow
 * client-server requests
 *  and get timing data, indicating how much time was elapsed in the various processing steps. 
 *  <P>
 * Cypress tests can make use of these capabilities to log timing information for slow requests,
 * and 
 *  optionally cause the test to fail.
 *  <P>
 *  The primary APIs for this are as follows:
 *  <ul>
 * <li>{@link com.smartgwt.client.rpc.RPCManager#setTimingDataEnabled
 * RPCManager.setTimingDataEnabled()} - this method turns on logic to record 
 *       detailed timing data (client and server side) on every Smart GWT RPC transaction</li>
 * <li>{@link com.smartgwt.client.rpc.RPCManager#addProcessingCompleteCallback
 * RPCManager.addProcessingCompleteCallback()} - this allows you to register a callback 
 *       function to fire after every Smart GWT RPC transaction completes</li>
 * <li>{@link com.smartgwt.client.rpc.RPCManager#getTransactionDescription
 * RPCManager.getTransactionDescription()} - this method returns a brief description of
 *       a transaction</li>
 * <li>{@link com.smartgwt.client.rpc.RPCManager#getTimingData RPCManager.getTimingData()} -
 * returns the timing data for a transaction as a {@link
 * com.smartgwt.client.widgets.tree.Tree}</li>
 * <li>{@link com.smartgwt.client.rpc.RPCManager#getFormattedTimingData
 * RPCManager.getFormattedTimingData()} - this formats the timing data as a string 
 *      for logging purposes</li>
 *  </ul>
 *  The sample <code>commands.js</code> file includes a command <code>"enableSC_RPCTimeout"</code>
 * which makes use of <a href='https://nodejs.org/api/events.html#class-eventemitter'
 * target='_blank'>Event emitters</a>
 *  in Cypress to take advantage of these APIs and log timing information for slow requests.
 *  <P>
 * <code>enableSC_RPCTimeout</code> should typicaly be called only once, at the beginning of your
 * test. It will remain active
 *  as long as the page is loaded. The command takes the following arguments:
 * <ul><li><code>logThreshold</code> Any RPCs whose duration exceeds this threshold will log
 * timing information
 *          via a <code>cy.log()</code> 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 <code>.cy.js</code> 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;
 *  
 *  describe('Test Suite', () =&gt; { 
 * 
 *      beforeEach(() =&gt; {
 *          cy.visit(&lt;target url&gt;); 
 *          cy.enableSC_RPCTimeout(LOG_TIMEOUT, FAILURE_TIMEOUT, {logDetail:"detailed"});
 *      });
 *    
 *      it('Perform some test', () =&gt; {
 *          ... // 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> command described above.
 *  <P>
 *  <b>Drag and Drop Interaction</b>
 *  <p>
 * To achieve this goal, the easiest way is to use a specialized plugin for Cypress, where you
 * only 
 *  need to run the following command:
 *  <pre>
 *      npm install --save-dev @4tw/cypress-drag-drop
 *  </pre>
 *  Finally, you need to add the following code to the commands.js file:
 *  <pre>
 *      require("@4tw/cypress-drag-drop");
 *  </pre>
 *  This way, you can perform drag and drop interactions by simply running the following command:
 *  <pre>
 *      cy.get('@source').drag('@target')
 *  </pre>
 *  where '@source' is obtained via <i>cy.get()/getSC(&lt;locator&gt;).as('source')</i> and 
 *  '@target' via <i>cy.get()/getSC(&lt;locator&gt;).as('target').</i>
 *  <P>
 *  <b>Final Note</b>
 *  <P>
 *  In addition to the custom commands and approaches described above, 
 *  developers can also always use the
 *  <a href='https://docs.cypress.io/api/commands/window' target='_blank'>cy.window()</a> command 
 *  to execute arbitrary JavaScript with access to the application scope.
 *  <P>
 *  <b>Cypress configuration for custom Smart GWT commands</b>
 *  <P>
 *  The custom commands shipped in the Smart GWT SDK will respect the following 
 *  settings if present in the Cypress configuration:
 *  <ul>
 *  <li><i>scLogCommands</i>: Boolean - if true each command will be logged via cy.log()</li>
 * <li><i>scCommandTimeout</i>: Number - default timeout for getSC() and waitForSCDone() in
 * ms</li>
 *  </ul>
 */
public interface SmartClientCypress {
}
