Cross-Browser Testing With TestComplete

TestComplete 9

Applies to: TestComplete 8.60


The goal of cross-browser testing is to make sure that your website or web application behaves correctly in any browser. This is sometimes very challenging and can cause many QA departments a lot of headache. Performing multiple browser verifications manually is time consuming, since most of the browser versions cannot co-exist with each other.

To automate the visual layout of cross-browser testing it is better to apply specialized web-services like Browsershots, BrowserCam and others. These services generate a series of screenshots taken in various browsers and operating systems, so that you can easily find what is wrong or if everything displays correctly.

These tools are alright for testing the visual appearance. But often the program logic depends on the browser and OS version more so than the visual appearance. What should we use to test how the website, server and application behave and react to user actions in different browsers? Not to mention, do all of this automatically.

TestComplete can automatically perform specific actions over a website, server app and/or web application and log the results of your web testing. You can even launch your TestComplete web tests under various operating systems and with various web browser versions.

TestComplete 8.60 works with Windows 2000 all the way up to Windows 7 (including 64-bit editions) and supports Internet Explorer ver. 5 - 9; Mozilla Firefox ver. 3 - 6 and browsers based on the Microsoft WebBrowser control. So, you can test a wide variety of configurations and combinations using both test labs with a lot of physical computers or virtual test labs using virtual machines like Virtual PC, Virtual Server, VMWare Workstation, VMWare Server or any other similar tools. Also, to automate the virtual machine management you can use Automated Build Studio.

Creating a web test that will work in any supported browser is not as easy as it sounds. The user interface and inner object hierarchy of different browser versions is not always the same, let alone the structure of browsers is always different depending on the vendor. For example, a test recorded with Internet Explorer 6 will most likely not work right out of the box in Mozilla Firefox 3.

However, as hard as this may sound, it’s still possible, but we need to take the browser differences into account when creating web tests.

The purpose of this article is to provide a general concept of creating cross-browser tests in TestComplete and to provide you with solutions to some of the most common problems. To illustrate, with real-world code snippets, we created a sample project that will work with TestComplete’s Web Test Sample (<TestComplete Samples>\Scripts\WebTest\) in a cross-browser test.

Organizing a Cross-Browser Testing Project

One of the most fundamental rules of QA is to “plan your test thoroughly”. So, before we start coding let’s clarify what we are going to do and how we will do it.

The TestComplete project should:

  1. Get a list of currently available browsers.
  2. Launch the first browser.
  3. Prepare for the web test.
  4. Perform web test or tests:
    • Open the web page for testing.
    • Perform the web testing actions.
    • Log the web testing results.
  5. Close the browser.
  6. Launch another browser from the list.
  7. Repeat steps 3-6 for all browsers.

Performing a Cross-Browser Testing Web Test

Your cross-browser test should take into account the specific technology of the browsers, so that it can be executed in any browser. To do this, the Scripting and Name Mapping project items are needed in TestComplete.

In our web testing scripts the browser specifics will be handled by branching the code with if or switch/case constructs. That is, different code fragments will be executed depending on the currently selected browser. The Cross-Browser Testing via Scripts section describes how to organize the branch and access the page elements from different browsers. The XBrowsing (Script) project in the sample TestComplete project suite demonstrates this approach.

The Name Mapping feature can be applied to address the page elements shown in different browsers by the same mapped name. The Cross-Browser Testing via Scripts and Name Mapping section describes how to create browser-independent tests combining scripts and name mapping. The XBrowsing (Script and Name Mapping) project of the supplied sample TestComplete project suite illustrates this concept.

Cross-Browser Testing via Scripts

The code samples are in JScript, but the tests can be implemented in any other scripting language supported by TestComplete.

Taking into account the information provided in the previous sections, we can create the main procedure:

// Global variables
var browser; // Object that stores currently used browser process
var browserID; //Short identifier for the browser name and version.

function Main()
    // Get a list of browsers

    for (var i=0; i<TestedApps.Count; i++)
        // Launch the browser
        browser = TestedApps.Items(i).Run();

        // Prepare TC for the current browser

        // Perform web-tests in the current browser

        // Close the browser process
    Log.Error("Exception: "+ exception.description, exception.description);
    //Clear the TestedApps list
    while (TestedApps.Count>0)

Getting a List of Browsers for Cross-Browser Testing

Typically, there are several browsers in the operating system. One instance of Internet Explorer is always available, since it is an essential part of Windows (yet, multiple versions of IE are not allowed). There can be several applications that use the WebBrowser control. Besides, different versions of the Mozilla Firefox browser can co-exist simultaneously (when installed to separate folders and use separate profiles).

Hence, we need to obtain a list of browsers that are available in the current operating system. The code snippet below performs this task. It retrieves the path to Internet Explorer from the list of registered COM servers (since IE is such a server), searches the registry for paths to the installed versions of the Firefox browser and adds the found browsers to the TestedApps collection.

// Retrieve a list of installed Mozilla and Microsoft browsers
// and add them as TestedApps
function GetBrowsers()
  var keyName, regType, regKey, ffVerName, ffBrowserKey, appIdx;

  //Retrieve info on installed Firefox versions from the registry
  keyName = "SOFTWARE\\Mozilla\\Mozilla Firefox\\";
  regType = 0;  // AQRT_32_BIT
  if (Sys.OSInfo.Windows64bit == true)
    keyName = "SOFTWARE\\Wow6432Node\\Mozilla\\Mozilla Firefox";
    regType = 1; // AQRT_64_BIT
  regKey = Storages.Registry(keyName, HKEY_LOCAL_MACHINE, regType, true);
  if (regKey.SectionCount == 0)
    Log.Message("Firefox is not installed.");

  for (var i = 0; i < regKey.SectionCount; ++i)
    ffVerName = regKey.GetSectionName(i);
    ffVerName = aqString.SubString(ffVerName,0,3);
    // Add only supported versions of Firefox (3.5 - 6.0)
    if ((ffVerName>=3.5)&&(ffVerName<7))
      ffBrowserKey = regKey.GetSubSectionByIndex(i).GetSubSection("Main");
      if (ffBrowserKey.OptionExists("PathToExe"))
         appIdx=TestedApps.Add(ffBrowserKey.GetOption("PathToExe", ""));

  //Retrieve info on the installed Internet Explorer version  

Preparing the Web Test for Cross-Browser Testing

This step includes items that you may need to perform in order to prepare the currently selected browser, TestComplete or the environment for cross-browser testing. Quite often, you don't need any special configurations, and this step may be skipped. In other cases, you may need to - configure the browser settings, assign project or project suite variables, load the appropriate name mapping scheme and so on.

In the sample project we'll use this stage to initialize global variables and handle Firefox Update dialogs that may appear on Firefox start.

function PrepareForBrowser()
  // Calculate a helper variable that holds the browser name and version
  browserID = aqString.ToLower(browser.ProcessName) + browser.FileVersionInfo.MajorPart;

  if (isMozilla())
    //Handle Firefox Update dialogs and windows that appear on Firefox start
    var updateDlg=browser.WaitWindow("*", "Firefox Update", -1, 3000);
    if (updateDlg.exists)
      // When a Firefox Update is invoked,
      // the Firefox is automatically relaunched
      // and we need to re-obtain the browser process
      browser = Sys.WaitProcess("firefox",3000);

Getting References to a Web Page in Cross-Browser Testing

TestComplete adds a special Page object to each of the supported browsers. This object is a wrapper for the browser control that displays web pages. However, the Page object is typically deep inside the object hierarchy, which varies a lot from browser to browser and from one version to another. For instance, the hierarchy of Internet Explorer 7 and later versions includes windows that correspond to the browser’s tabs (these windows are absent in the hierarchy of Internet Explorer 5 and 6). The window hierarchy of Firefox 4 and later versions also contain extra windows that were absent in the hierarchy of Firefox 3. To learn more about the specifics of handling pages opened in tabs, see the Web Testing and Tabbed Browsers technical article.

Since version 6.0, TestComplete has the “Make Page object a child of the browser process” option that simplifies accessing the Page object from Internet Explorer and Firefox. When the option is enabled, the object is duplicated and becomes a child of the iexplore or firefox process.

Moreover, the processes that correspond to these browsers also get the process.ToUrl() method that navigates to the desired page and returns the appropriate Page object on success. We will use this method to open the page to be tested.

The Page object has methods and properties that are common for all onscreen objects and adds a number of specific methods and properties that let you navigate through web pages in the browser, search for elements of a web page and perform other operations. That is, by addressing the Page object you can avoid problems that might appear when you are working with the user interface.

Here is an example that illustrates this: The location and program hierarchy of the Address edit box is different for each of the browsers. So, opening a page by entering the desired URL via the user interface is browser-specific. The Page.ToUrl method allows you to open the specified address in every supported browser.

There is another feature of the Page object, which is even more important. The object acts as a parent for the rest of the elements of a web page (see the next section), and it also exposes the members of the underlying rendering engine.

Depending on the browser family, the rendering engine is either Microsoft’s WebBrowser or Mozilla's Window.

Microsoft Engine in Object Browser

Microsoft Engine in Object Browser

Mozlla Engine in Object Browser

Mozilla Engine in Object Browser

The engines have their own set of methods and properties, so we should determine which engine is currently used before referring to its members. This is pretty simple - different versions of Firefox use Mozilla’s engine, all other supported browsers use Microsoft’s engine. So, all we have to do is check whether the browser is Mozilla Firefox:

function isMozilla()
  return ( aqString.Find(browser.FullName, 'Process("firefox"', 0, false) !=-1)

To make the test reproducible on either of the browser families, we need to organize a minimum of two code branches - one for the Microsoft engine and one for Mozilla. Below are some typical examples:

  • Assume you need to navigate to one of the previous URLs stored in the history list. If you record a click over the Back button, this will be a browser-specific action (since all browsers have different GUIs), and you will need separate instructions for each of the browsers. However, if you refer to the engine’s methods, then only two code branches will be needed.

    ... if (isMozilla()) PageObj.back(); else PageObj.GoBack(); ...
  • If a page contains active content the Internet Explorer browsers may display notification message that ask the user if the content is permitted, whereas, the Mozilla browsers do not notify the user in the same situation. To playback the test on any of the browsers, we should handle the notifications in IE. So, the code below may be required in this case:

    if (!isMozilla()) AllowBlockedContent()

Accessing Elements of a Web Page for Cross-Browser Testing

All page elements are shown as child nodes of the Page object. To represent the hierarchy of web page elements, TestComplete uses one of four different object models: DOM, Tag, Tree or Hybrid. The chosen web tree object model defines how an element is addressed during test recording and playback. For instance, the same button will be named:

  • ...Page(“NNN”).document.all.Item(“btnGo”) in the DOM model,
  • ...Page("NNN").INPUT.Item("btnGo") in the Tag model,
  • ...Page("NNN").Form("f").Table(0).Cell(0, 1).SubmitButton("btnGo") in the Tree and Hybrid models.

Therefore, a test should be played back using the same web tree model it was recorded with. At design time, the web tree model is set via the project’s Tree model property; at run-time it can be checked and set via the Options.Web.TreeModel property.

If the desired element can be uniquely identified by one or more of its properties (Name, ID, tagName, text, innerHTML and others), you can avoid discrepancy in the object model names by using the Page.NativeWebObject.Find method. This method searches the web page for the element that contains the property value of the given object and returns the found element on success. So, for instance, the code myGoBtn=Page("NNN").NativeWebObject.Find("id", "btnGo") will work fine regardless of the chosen web tree model.

In your cross-browser tests you can use whatever approach you like. However, if the test refers to a lot of elements, then multiple calls to Page.NativeWebObject.Find may slowdown the cross-browser test, therefore in this case it is recommended to set the web tree model and later on address the elements using the model notation.

After you have found the needed element, you can refer to its members. In TestComplete, scripting objects that correspond to elements of a web page contain the following members:

  • Methods and properties defined by the Document Object Model.
  • Methods and properties defined by the HTML 4 standard.
  • Methods and properties provided by TestComplete, namely:
    • Methods and properties common for all tested objects.
    • Visual objects that contain methods, properties and actions common for onscreen objects.
    • Objects that correspond to combo box, list box and check box elements and include control-specific methods, properties and actions.
    • Additional methods and properties provided by the selected web tree model. For instance, the Tree model adds the RowIndex and ColumnIndex properties to the Cell objects (these objects correspond to the TD and TH elements).
  • Methods and properties specific to the browser family:
    • Microsoft Internet Explorer or the WebBrowser control
      The list of Microsoft browser-specific element members is provided by: HTML and DHTML Reference on Microsoft Developer’s Network.
    • Mozilla Firefox
      For information on methods and properties provided by Mozilla Firefox, see Gecko DOM Reference on Mozilla Developer Center.

If you want to create a reliable test, you should use browser-specific methods and properties as little as possible. If calls to browser-specific members are inevitable, you should use all possible alternatives, so, the if and switch/case constructs are also welcomed here.

Cross-Browser Testing via Scripts - Sample

Now, let’s see how to create a cross-browser test using TestComplete’s Web Test Sample. The routines described here are listed in the XBrowsing (Script) project.

The web test routine in our cross-browser test retrieves an arbitrary Page object, navigates to the Web Test Sample page (<TestComplete Samples>\Scripts\WebTest\MainPage.htm), switches to the “Tree” web model, and then simulates actions over different types of web controls.

function DoWebTest()
  var page, WebControlsURL, modalDlg;

  WebControlsURL = "file:///" + Project.Path + "..\\" + "MainPage.htm";
  // Get the Page object
  // and navigate to Web Test Sample page


  if (!page.Exists) {Log.Error("Unable to open the tested page."); return;}
  // Enable ActiveX controls in IE
  if (!isMozilla(page)) AllowBlockedContent();

  // Set Web Tree model

  // Test Singleline Edit
  page.Table(0).Cell(0, 0).Textbox("Input").value = "Some new text";
  // Test Multiline Edit
  page.Table(0).Cell(0, 1).Textarea("textarea").Keys("[Del]teststring1[Enter]teststring2[Enter]teststring3");
  // Test Radiobuttons
  page.Table(0).Cell(0, 2).Label(1).RadioButton("RadioGroup1").checked = true;
  page.Table(0).Cell(0, 2).Label(2).RadioButton("RadioGroup1").checked = true;
  page.Table(0).Cell(0, 2).Label(3).RadioButton("RadioGroup1").checked = true;
  // Test Checkbox
  page.Table(0).Cell(1, 0).Label(0).Checkbox("checkbox").checked = true;
  page.Table(0).Cell(1, 0).Label(1).Checkbox("checkbox").checked = false;
  page.Table(0).Cell(1, 0).Label(2).Checkbox("checkbox").checked = true;
  // Test Combobox
  page.Table(0).Cell(1, 1).Panel(0).Select("select").selectedIndex=2;
  // Test Listbox
  page.Table(0).Cell(1, 2).Panel(0).Select("select2").selectedIndex=3;
  // Test Buttons
  page.Table(0).Cell(2, 0).Panel(0).Button("Button").Click();
  page.Table(0).Cell(2, 0).Panel(0).Button("Button_2").Click();
  page.Table(0).Cell(2, 0).Panel(0).Button("Button_3").Click();

  // Invoke Modal Dialog
  page.Table(0).Cell(2, 2).Panel(0).Button("ButtonM").Click();
  // Input data to dialog
  switch (browserID)
    case "iexplore8": case "iexplore9":
           modalDlg = Sys.FindChild("WndCaption","Explorer User Prompt",3);
    case "iexplore7": case "iexplore6": case "iexplore5":
           modalDlg = browser.FindChild("WndCaption","Explorer User Prompt",3);
    case "firefox3": case "firefox1":
           modalDlg = browser.FindChild("WndCaption","[JavaScript Application]",3);
    case "firefox2": case "firefox5":  case "firefox6":
           modalDlg = browser.FindChild("textContent","[JavaScript Application]",5);
  if (modalDlg.Exists)
      modalDlg.Keys("New Caption[Enter]");
      Log.Message("Caption changed.");
    else Log.Error("Unable to find modal dialog");

  // Verify Links
  VerifyLink(page, page.Table(0).Cell(2, 1).Panel(0).Link(0));
  VerifyLink(page, page.Table(0).Cell(2, 1).Panel(0).Link(1));
  VerifyLink(page, page.Table(0).Cell(2, 1).Panel(0).Link(2));

Below are several notes regarding the code snippet above:

  1. As you can see, a major part of the code is common for all browsers. To differentiate between the different browsers, branching is applied.

    Pay special attention to the fragment where the scripting object that corresponds to the modal dialog is obtained. It takes into account the following browser specifics: in Firefox version 3 and earlier the modal dialogs have the “JavaScript Application” caption and are Window objects; in Firefox 4, 5 and 6 modal dialogs have the same caption but are vbox XUL objects; in Internet Explorer modal dialogs are entitled as “Explorer User Prompt”. Due to Microsoft’s Loosely Coupled IE (LCIE) concept, the modal dialogs in Internet Explorer 8 and 9 are invoked in the helper tab process. Hence, we need to search for the dialog object within the entire Sys object rather than only within the browser process.

  2. When the routine is executed under Internet Explorer, it additionally calls the AllowBlockedContent routine that permits the ActiveX controls to access the computer.

    The routine’s code is the following:

    //Enables the active content which is blocked by default
    function AllowBlockedContent(browser)
      var InfoBar, ConfirmDlg;
       // Search for the Internet Explorer Information bar
       // (It always belongs to the master instance of IE)
        switch (Project.Variables.browserID)
        case "iexplore9":
               InfoBar=Sys.FindChild("WndClass","Frame Notification Bar",3);
               if (InfoBar.Exists)
                  if (InfoBar.Visible)
                  var wnd = InfoBar.Window("DirectUIHWND");
                  wnd.Click(wnd.Width-40 , wnd.Height-20);
        case "iexplore8":
               InfoBar=browser.FindChild("WndCaption","To help protect your security*",15,true);
               if (InfoBar.Exists)
                  if (InfoBar.Visible)
                InfoBar.PopupMenu.Click("Allow Blocked Content...");
                // Search for the confirmation dialog
                // In IE8 it is child tab-process
                ConfirmDlg=Sys.FindChild("WndCaption","Security Warning",3);
                ConfirmDlg.Window("Button", "&Yes").ClickButton();
      default: // IE7 and earllier
               InfoBar=browser.FindChild("WndCaption","To help protect your security*",15,true);
               if (InfoBar.Exists)
                  if (InfoBar.Visible)
                InfoBar.PopupMenu.Click("Allow Blocked Content...");
                // Search for the confirmation dialog
                // In IE7 it is in the same process instance
                ConfirmDlg=browser.FindChild("WndCaption","Security Warning",5);
                ConfirmDlg.Window("Button", "&Yes").ClickButton();
  3. To verify links, the VerifyLink routine is applied. The routine simulates a click over the link and then checks the opened page. When you follow an invalid link, the browsers display different types of error messages. The routine analyzes the class names of the page elements and if they contain the word “error”, then it considers that link as invalid.

    The routine has three input parameters: browser - the Process object that corresponds to the current browser; parentPage - the Page object that contains the link to be verified; linkElement - the object that corresponds to the <A> element in HTML.

    //Check the URL a link leads to
    //  PageObj - the Page object that contains the verified link
    //  linkElement - the object that corresponds to a element of HTML
    function VerifyLink(PageObj, linkElement)
      var newUrl, originalURL, err;
      //Get the initial and desired URLs

      // Check whether the "ie8_href" property is supported
       if ( aqObject.IsSupported(linkElement, "ie8_href") )

      //Follow the link

      PageObj=Sys.FindChild("Name","Page(" + newUrl + ")",15);
      if (PageObj.Exists)
        //Search for the objects which contain "error" in their name
        // In Firefox these are "errorTryAgain" "errorContainer"
        // In Internet Explorer these are "ErrorInAddress", "errorDescr" "errorExpl"
        if (err.Exists)
          Log.Warning("Link " + newUrl + " does not exist",err.FullName);
        else Log.Message("Link " + newUrl + " - success");
      else Log.Warning("Link " + newUrl + " does not exist");
      //Revert to initial URL

Cross-Browser Testing via Scripts and Name Mapping

The name mapping feature assigns a custom name to objects and uses mapped names in your tests instead of real ones. By creating name mapping aliases, you can "mask" the actual hierarchy of program objects. In addition to giving a custom name, you also need to provide a set of properties and their values to be applied as recognition attributes (otherwise, TestComplete won't find the mapped objects). Since TestComplete 8, it is possible to define conditional mapping criteria, that is, to specify several sets of recognition attributes.

You can apply conditional name mapping to create cross-browser tests. The same mapped name can be used to refer to web page elements in different browsers. So, the resulting script will be uniform, but for each browser, the appropriate underlying object will be selected. In this way, you can avoid code branching in scripts.

The tricky part of this approach is in defining the appropriate mapping criteria. Unfortunately, automatic name mapping won't be helpful in this case, so you need to define recognition attributes manually. This means that you need to map not only the desired object, but all of its parent objects as well. Typically, the object hierarchy that includes web page elements looks like this:

  1. The Sys object.
  2. The browser process object.
  3. A number of window objects. The last window contains the web page.
  4. The Page object.
  5. The document object (for the DOM and Hybrid tree model).
  6. The all collection or tag-named collections (for DOM and Tag tree models).
  7. Web page elements.

Mapping Page Elements

Mapping Page Elements

While creating the name mapping scheme, we need to comply with the recommendations given below to make the cross-browser test:

  • To map the Process object, use the CommandLine and FileVersionInfo properties in addition to the ProcessName property that is suggested by default. Besides, you need to switch to the Required Children property and mark the item that corresponds to the tested web page. This helps take into account the diversity between different browser versions. For example, how Internet Explorer 8 uses multiple executables for each tab that is displayed.
  • When mapping the Page object use its URL as a recognition attribute. If the page is formed dynamically, it may contain a query string (parameters passed after the question mark in the URL). To make the mapped settings independent of the parameter values, you can use the asterisk (*) wildcard in place of the query values -- Page("*&sid=*"), or in place of the entire query string -- Page("*"). To make the mapped page accessible on any level of the hierarchy, enable the “Find this node on any level of NameMapping tree” property in the Object Name Mapping Dialog. Thus the test would not depend on the current value of the project’s “Make Page object a child of the browser process” option.
  • When mapping objects that correspond to web page elements, avoid using browser-specific properties as recognition attributes, use only those properties that are available in every browser. To find common properties open the same tested page in different browsers and use the Object Browser to find the desired element and compare the properties.

Cross-Browser Testing via Scripts and Name Mapping - Sample

To handle the diversities between browsers in the given sample project, we need to establish conditional name mapping criteria for the browser process and for the modal dialog.

Mapping Browser Process

Mapping Browser Process

Mapping Modal Dialog

Mapping Modal Dialog

The name mapping recognition attributes used, as well as well as the listed routines can be found in the XBrowsing (Script and Name Mapping) project of the supplied TestComplete project suite.

When the name mapping configuration is loaded, we can address the web page and its elements by their mapped names ignoring the actual browser object hierarchy.

function DoWebTest()
  var page, WebControlsURL;
  WebControlsURL = "file:///" + Project.Path + "..\\" + "MainPage.htm";

  // Get the Page object
  // and navigate to Web Test Sample page
  page = Aliases.theBrowser.pageWebtestSamplePage;

  if (!page.Exists) {Log.Error("Unable to open the tested page."); return;}

  // Enable ActiveX controls in IE
  if (!isMozilla()) AllowBlockedContent();

  //Test Singleline Edit
  page.table.cell00.textboxInput.value = "Some new text";
  //Test Multiline Edit
  //Test Radiobuttons
  page.table.cell02.radiobutton1Radiogroup1.checked = true;
  page.table.cell02.radiobutton3Radiogroup1.checked = true;
  page.table.cell02.radiobutton2Radiogroup1.checked = true;
  //Test Checkbox
  page.table.cell10.checkboxCheckbox1.checked = true;
  page.table.cell10.checkboxCheckbox2.checked = false;
  page.table.cell10.checkboxCheckbox3.checked = true;
  //Test Combobox
  //Test Listbox
  //Test Buttons

  //Test Modal Dialog
  if (Aliases.theBrowser.dlgModalEnterCaption.Exists)
      Aliases.theBrowser.dlgModalEnterCaption.Keys("New Caption[Enter]");
      Log.Message("Caption changed.");

    else Log.Error("Unable to find modal dialog");

  //Verify Links
  VerifyLink(page, page.table.cell21.linkTestlink1);
  VerifyLink(page, page.table.cell21.linkTestlink2);
  VerifyLink(page, page.table.cell21.linkTestlink3);


By following the above-mentioned recommendations, you can create a test that will work in any supported browser and make your automated web tests into cross-browser tests. Of course, it’s impossible to go over all possible scenarios with cross-browser testing, so there will be some trial and error work in your test development. 

Be sure to check out SmartBear's official TestComplete training package as well.

Related Links

Useful recommendations on running multiple versions of multiple browsers on the same computer:

Reviews of services for checking the site visual appearance: