public override void Run(IRootElement root, IWebBrowser browser, IScope scope)
        {
            if (Command is null)
            {
                throw new TestException("InvokeServerCommand. Command is required");
            }
            if (Path is null)
            {
                throw new TestException("InvokeServerCommand. Path is required");
            }

            var id               = Id.ResolveValue(root) ?? "";
            var invokeScript     = $"return window.__tests__.$invoke({{target: 'shell', action: 'invoke', cmd: '{Command}', path: '{Path}', id:'{id}'}});";
            var lastResultScript = $"return window.__tests__.$lastResult();";

            var result = browser.ExecuteScript(invokeScript);

            result = browser.ExecuteScript(lastResultScript);

            if (result == null)
            {
                throw new TestException($"Error invoking command '{Command}'");
            }

            // 'sucess:id' or 'error:exception message here'
            if (result.StartsWith("success:"))
            {
                root.SetValue(To, result.Substring(8));
            }
            else if (result.StartsWith("error:"))
            {
                throw new TestException(result.Substring(6));
            }
            else
            {
                throw new TestException($"Invalid response value: '{result}'");
            }
        }
        public override void ElementRun(IRootElement root, IWebBrowser browser, ITestElement elem)
        {
            ITestElement row = null;

            if (!String.IsNullOrEmpty(Id))
            {
                var id     = Id.ResolveValue(root);
                var script = $"return window.__tests__.$invoke({{target: 'datagrid', testId: '{Parent.TestId}', action: 'selectRow', id: '{id}'}});";
                var result = browser.ExecuteScript(script);
                if (result == null || result != "success")
                {
                    throw new TestException($"Could not select element with id='{id}'");
                }
                WaitClient();

                row = elem.GetElementByXPath(".//tr[contains(@class, 'dg-row') and contains(@class, 'active')]");
            }
            else if (!String.IsNullOrEmpty(Text))
            {
                row = elem.GetElementByXPath($".//tr[contains(@class, 'dg-row')]/td//span[normalize-space()={Text.XPathText()}]/ancestor::tr");

                var cell = row.TryGetElementByXPath("./td[@class='details-marker']");
                if (cell == null)
                {
                    cell = row.TryGetElementByXPath("./td/span[@class='dg-cell']/..");
                }
                if (cell == null)
                {
                    cell = row.TryGetElementByXPath("./td/span[contains(@class, 'span-sum')]/..");
                }
                if (cell == null)
                {
                    throw new TestException("Could not find applicable cell in DataGridRow");
                }
                cell.Click();
                WaitClient();
            }

            if (row == null)
            {
                throw new TestException("DataGridRow. Attributes 'Id' or 'Text' are required");
            }


            foreach (var step in Steps)
            {
                step.ElementRun(root, browser, row);
            }
        }
        public override void ElementRun(IRootElement root, IWebBrowser browser, ITestElement control)
        {
            var testId = "null";

            if (!String.IsNullOrEmpty(Parent?.TestId))
            {
                testId = $"'{Parent.TestId}'";
            }
            var script = $"return window.__tests__.$invoke({{target: 'controller', testId: {testId}, action: 'eval', path: '{Path}'}});";
            var result = browser.ExecuteScript(script);

            if (result == null)
            {
                throw new TestException($"Could not evaluate expression '{Path}'");
            }
            root.SetValue(To, result);
        }