Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using C# and Selenium to enter multi lined SQL text into a Code Mirror textbox on a webpage

I am trying to use Selenium to enter SQL code into a Code Mirror textbox. I'll use the site http://www.gudusoft.com/sqlflow/#/ as an example for the purposes of this question.

My Problem:

I am unable to submit MSSQL SQL code into the code text box if the code contains a carriage return or line feed.

As a work around, I am removing all of them before writing it to the textbox using a JavaScript function but the end result is very ugly word-wrapped SQL.

I've also tried using the SendKeys method on a Selenium webElement object to send the code into the textbox, but I am unsure which element to "Find." Using SendKeys requires that the textbox be selected and when I try to invoke the Click" and "SendKeys" method on that object, I often get a error that the element does not permit user interaction.

If I could consistently find an element that I could interact with, like a TextArea, I would try to paste the contents of my clipboard into it rather than send a very large number of keystrokes to the textbox. For example, the following usually gives me the "unable to interact with this object" error but occasionally works, depending on the current content of the textbox, presumably.

Clipboard.SetText(sql);
var txtbx = codeMirror.FindElement(By.CssSelector("textarea"));
txtbx.Click();
txtbx.SendKeys(OpenQA.Selenium.Keys.Control + "v");
       

I'm thinking that my best chance of setting the text is using the Execute script method to execute the setValue JavaScript method on the CodeMirror object as shown below. Again, this works if the SQL has no CR / LF characters but how do I change my code to allow for these characters?

I've seen many many postings on this but my JavaScript knowledge may not be good enough to get me to the end result. I'm hoping that someone can reconstruct a working example using the following code. Here's the relatively short instructions.

Create a C# project (Console app, winForms, etc) and add the following 3 Nuget packages:

Selenium.Chrome.WebDriver
Selenium.WebDriver
Selenium.WebDriver.ChromeDriver

Create a class "SeleniumHelperGudusoft" and paste in the following code:

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;

namespace SqlSmoke.Classes
{
    public class SeleniumHelperGudusoft
    {
        private IWebDriver driver;

        public SeleniumHelperGudusoft()
        {
            var chromeDriverService = ChromeDriverService.CreateDefaultService();
            chromeDriverService.HideCommandPromptWindow = false;

            ChromeOptions options = new ChromeOptions();
            options.AddAdditionalCapability("useAutomationExtension", false);
            this.driver = new ChromeDriver(chromeDriverService, options);
        }

        public void NavigateToMain()
        {
            driver.Url = @"http://www.gudusoft.com/sqlflow/#/";
        }

        public void SetLanguageToMsSql()
        {
            string languageButtoncssSelector = "#Root > div > div.Main > div > div.Route.Row.x-start.y-stretch > div.SQLFlowEditor > div.SQLFlowEditorOperations.Row.x-start.y-center > div.DbVendor > div > div > svg";
            var languageButton = driver.FindElement(By.CssSelector(languageButtoncssSelector));
            languageButton.Click();

            string msSqlCssSelector = "#Root > div > div.Main > div > div.Route.Row.x-start.y-stretch > div.SQLFlowEditor > div.SQLFlowEditorOperations.Row.x-start.y-center > div.DbVendor > ul > li:nth-child(10)";
            var msSql = driver.FindElement(By.CssSelector(msSqlCssSelector));
            msSql.Click();
        }

        public void SetSqlText(string sql)
        {
            IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
            var codeMirror = driver.FindElement(By.ClassName("CodeMirror"));
            js.ExecuteScript("arguments[0].CodeMirror.setValue(\"" + sql + "\");", codeMirror); //<<<<----Fails here with the error message shown below in my post
        }

        public void ClickVisualizeButton()
        {
            string buttonCssSelector = "#Visualize > div";
            var button = driver.FindElement(By.CssSelector(buttonCssSelector));
            button.Click();
        }
    }
}

Exercise the code in the above class to try pasting in two different SQLs, one without line feed characters and one with.

string sql;

var lineageHelper = new SeleniumHelperGudusoft();
lineageHelper.NavigateToMain();
lineageHelper.SetLanguageToMsSql();

sql = "SELECT COL1, nCOL2 FROM TABLE1"; //all on one line works
lineageHelper.SetSqlText(sql);
lineageHelper.ClickVisualizeButton();
                    
sql = "SELECT COL1, \r\nCOL2 FROM TABLE1"; 
lineageHelper.SetSqlText(sql); //<<<----- Fails here with the following error message
lineageHelper.ClickVisualizeButton();

I get the following error on the second call to the SetSqlText method on the line:

js.ExecuteScript("arguments[0].CodeMirror.setValue(\"" + sql + "\");", codeMirror);

Message "javascript error: Invalid or unexpected token\n  (Session info: chrome=84.0.4147.105)" string

How can I modify the example to get the second query to be entered into the CodeMirror textbox?

Update

Code Mirror documentation is found here: https://codemirror.net/doc/manual.html

Here's a full Call stack of the error:

OpenQA.Selenium.WebDriverException
  HResult=0x80131500
  Message=javascript error: Invalid or unexpected token
  (Session info: chrome=84.0.4147.105)
  Source=WebDriver
  StackTrace:
   at OpenQA.Selenium.Remote.RemoteWebDriver.UnpackAndThrowOnError(Response errorResponse)
   at OpenQA.Selenium.Remote.RemoteWebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
   at OpenQA.Selenium.Remote.RemoteWebDriver.ExecuteScriptCommand(String script, String commandName, Object[] args)
   at OpenQA.Selenium.Remote.RemoteWebDriver.ExecuteScript(String script, Object[] args)
   at MyNAME.Selenium.SeleniumHelperGudusoft.SetSqlText(String sql) in C:\Users\MYLANID\Desktop\SqlSmoke Code\MyNAME.Selenium\SeleniumHelper.cs:line 41
   at MyNAME.Selenium.Form1.button3_Click(Object sender, EventArgs e) in C:\Users\MYLANID\Desktop\SqlSmoke Code\MyNAME.Selenium\Form1.cs:line 201
   at System.Windows.Forms.Control.OnClick(EventArgs e)
   at System.Windows.Forms.Button.OnClick(EventArgs e)
   at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
   at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
   at System.Windows.Forms.Control.WndProc(Message& m)
   at System.Windows.Forms.ButtonBase.WndProc(Message& m)
   at System.Windows.Forms.Button.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
   at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
   at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.Run(Form mainForm)
   at MyNAME.Selenium.Program.Main() in C:\Users\MYLANID\Desktop\SqlSmoke Code\MyNAME.Selenium\Program.cs:line 19
like image 475
Chad Avatar asked Aug 12 '20 17:08

Chad


People also ask

What is C in used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

How do I use C on my computer?

It is a bit more cryptic in its style than some other languages, but you get beyond that fairly quickly. C is what is called a compiled language. This means that once you write your C program, you must run it through a C compiler to turn your program into an executable that the computer can run (execute).

How useful is C programming?

C is very fast in terms of execution time. Programs written and compiled in C execute much faster than compared to any other programming language. C programming language is very fast in terms of execution as it does not have any additional processing overheads such as garbage collection or preventing memory leaks etc.

Is C hard to learn?

C is more difficult to learn than JavaScript, but it's a valuable skill to have because most programming languages are actually implemented in C. This is because C is a “machine-level” language. So learning it will teach you how a computer works and will actually make learning new languages in the future easier.

What is this C tutorial for?

This tutorial is designed for software programmers with a need to understand the C programming language starting from scratch. This C tutorial will give you enough understanding on C programming language from where you can take yourself to higher level of expertise.

What are the uses of C programming language?

C was initially used for system development work, particularly the programs that make-up the operating system. C was adopted as a system development language because it produces code that runs nearly as fast as the code written in assembly language. Some examples of the use of C are - Operating Systems. Language Compilers. Assemblers. Text Editors

What is the use of using statement in C?

The "Using" Statement in C#. The using statement in C# defines a boundary for the object outside of which, the object is automatically destroyed. The using statement is exited when the end of the "using" statement block or the execution exits the "using" statement block indirectly, for example - an exception is thrown.

What is the use of @C++ using keyword?

C++ using is one of the keywords that is used for to bring the some specific member functions from the namespace that can be started using the keyword. It will be the directive, declarative, and other even directives are also implemented.


Video Answer


2 Answers

When setting the value in JavaScript, you likely need to re-escape the carriage return and newline characters:

var sql = @"SELECT foo
FROM bar";
var jsString = sql.Replace("\r", "\\r")
                  .Replace("\n", "\\n");

js.ExecuteScript("arguments[0].CodeMirror.setValue(\"" + jsString + "\");", codeMirror);

The resulting JavaScript line would be:

arguments[0].CodeMirror.setValue("SELECT foo\n\rFROM bar")

Be aware that any double-quotes inside your SQL string will also need to be escaped so they do not prematurely end the JavaScript string:

var sql = @"SELECT foo AS '"bar"'
FROM baz";
var jsString = sql.Replace("\r", "\\r")
                  .Replace("\n", "\\n")
                  .Replace("\"", "\\\"");

js.ExecuteScript("arguments[0].CodeMirror.setValue(\"" + jsString + "\");", codeMirror);

So the resulting JavaScript is:

arguments[0].CodeMirror.setValue("SELECT foo AS '\"bar\"'\n\rFROM baz");
like image 155
Greg Burghardt Avatar answered Oct 31 '22 14:10

Greg Burghardt


To avoid the error simply provide the SQL string as an argument:

js.ExecuteScript("arguments[0].CodeMirror.setValue(arguments[1]);", codeMirror, sql);

Or use back tick quoting:

js.ExecuteScript("arguments[0].CodeMirror.setValue(`" + sql + "`);", codeMirror);
like image 34
Florent B. Avatar answered Oct 31 '22 14:10

Florent B.