Testing IMCE Wysiwyg Integration in Drupal Using Behat and Mink

by Aaron Froehlich

Over the past few months we've been using Behat and Mink for acceptance testing on a large Drupal distribution we're building (further background reading here). Overall, the transition from our comfy rails testing tools like RSpec and capybara-webkit has gone pretty smoothly, and we look forward to more articles about what we've learned in the coming weeks.

Last week, however, I hit a roadblock when testing IMCE integration with the Wysiwyg module. When a user clicks on the "Browser Server" button, IMCE opens a new window, but the Mink context does not automatically shift to the new window. Luckily, Mink provides the solution for this exact problem with its
session->switchToWindow(name) method (documentation). Unfortunately, CKEditor doesn't name the window when it opens, so it is impossible to target the window using the built-in method.

The problem presents an interesting dilemma. Patches are a good solution for bugs in modules/libraries, but leaving out an optional attribute isn't really a bug. Also, the minified javascript code makes it really hard to even write a patch that would work correctly when applied. Finally, it hardly makes sense to patch a file simply for the test suite.

To solve the dilemma, we simply needed to figure out where CKEditor was creating that window, and then utilize Behat's @BeforeSuite and @AfterSuite1 hooks to add a window name before the suite runs, and remove it after the tests finish running. In this case, we created a static method called strReplaceFile

private function strReplaceFile($file, $find, $replace) {
    $str = file_get_contents($file);
    $str = str_replace($find, $replace, $str);
    file_put_contents($file, $str);
}

After isolating the correct window.open function in CKEditor.js (where they are indeed passing null as the window name), we were able to run our handy method to give the window a name:

self::strReplaceFile(
     'sites/all/libraries/ckeditor/ckeditor.js',
     'c=window.open("",null,d',
     'c=window.open("","file_browser",d'
);

On teardown, we simply reverse it:

self::strReplaceFile(
     'sites/all/libraries/ckeditor/ckeditor.js',
     'c=window.open("","file_browser",d',
     'c=window.open("",null,d',
);

Our handy use of str_replace now allows us to create step definitions like the following (using the selenium driver):

/**
 * @Given /^I choose the file "([^"]*)" in the "([^"]*)" folder$/
 */
public function chooseTheFileInTheFolder($filename, $folder)
{
  $this->getSession()->switchToWindow('file_browser');
  $this->page->clickLink($folder);
  sleep(1); //Solves intermittent timeout issues
  $el = $this->page->find('xpath', '//span[text()="'. $filename .'"]');
  $el->doubleClick();
  $this->getSession()->switchToWindow(null); // Switches back to the main window
}

1Note that if the tests fail to complete for some reason, the ckeditor.js file will remain in it's edited state.
Are you passionate about programming and making the world a better place with your code? We want to hear from you!