Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor jasmine tests #2902

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Conversation

Mikk3lRo
Copy link
Contributor

@Mikk3lRo Mikk3lRo commented Oct 6, 2017

Summary

While this PR may seem a bit overwhelming judging from the number of files / lines added and removed... please take a moment to understand why.

It is a complete refactoring of all tests to make them a little DRY'er... well a lot I guess since it's only a bit over one third of the original code in size despite adding more comments.

It is a direct reaction to the last comment @koenpunt made in #2899

The tests are exactly the same as in current master.

How

The main idea is that tests that can be shared between prototype and jquery versions should be shared... Just like the Chosen "abstract" itself. As it turns out all (current) tests except the very first one actually can.

So I've written three functions for each "version":

  1. fw_basictest The very basic test to check for the Chosen global / jQuery function
  2. fw A wrapper around the needed jQuery / prototype functions that allow access to them through a common interface
  3. fw_testcase A function to initialize the many Chosen drops used in the tests and return an object with references to the most commonly used elements

These three functions take up about 50 lines for each jQuery and prototype. The rest of the code is shared. If more "specialty" functions are needed in the future they can (probably) quite easily be added to the fw wrapper

I've also added two shared helper functions that make it possible to both initialize and do common tasks with a lot less code - so it's clearer what is actually being tested:

  • testcase_html constructs the actual html for most tests from an array or object.
  • testcase initiates each test by calling testcase_html, then fw_testcase and finally adds common functions like open_drop, click_result, set_search and so on to this object before returning.

This means that every test can now go something like:

it "is super easy to initialize Chosen and perform common tasks", ->
  this_test = testcase ['opt1', 'opt2'], {search_contains: true}
  do this_test.open_drop
  expect(this_test.get_results().length).toBe 2
  this_test.set_search "pt2"
  expect(this_test.get_results().length).toBe 1
  expect(this_test.get_results()[0].html()).toBe 'o<em>pt2</em>'

...which I hope everyone will agree is more readable than before. Of course the biggest advantage is that this test works for both jquery and prototype.

For some tests the simple array is not enough, so it can accept an object instead:

it "can accept an object if values and labels are not equal", ->
  this_test = testcase {the_key_is: 'different from the label'}
  do this_test.open_drop
  expect(this_test.get_results().length).toBe 1
  expect(this_test.get_results()[0].text()).toBe "different from the label"
  this_test.click_result 0
  expect(this_test.get_val()).toBe 'the_key_is'

For cases that need optgroups or other "advanced" markup, we can still pass the HTML directly:

it "can accept a string instead of an array for more advanced HTML", ->
  this_test = testcase """
    <select multi data-placeholder="Select a few options">
      <optgroup label="A &amp; B">
        <option value="Item1">Item1</option>
        <option value="Item2">Item2</option>
      </optgroup>
    </select>
  """, {search_contains: true}
  do this_test.open_drop
  this_test.set_search 'em2'
  expect(this_test.get_result_groups().length).toBe 1
  expect(this_test.get_result_groups()[0].html()).toBe "A &amp; B"
  expect(this_test.get_results().length).toBe 1
  expect(this_test.get_results()[0].html()).toBe "It<em>em2</em>"

I'm expecting someone to argue that it is harder to know what actually happens, since everything passes through "layers" instead of being right there in the code for anyone to see... For example I'm not 100% sure the helper that constructs the HTML is actually a good idea... It might be easier for someone else to add a new test if every test simply starts with the "plain" HTML needed for that specific case.

On the other hand I don't have any doubt that having only one set of actual tests to maintain is a huge advantage - both for maintainers and "new" contributors like myself.

  • All changes were made in CoffeeScript files, not JavaScript files.
  • You used Grunt to build the JavaScript files and tested them locally.
  • You've updated both the jQuery and Prototype versions.
  • You haven't manually updated the version number in package.json.

References

Just the one mentioned above: #2899

@adunkman
Copy link
Contributor

Hmm, I’m definitely on the fence. I’d argue that these sorts of tests should likely not be often changed, because a change in the test is either a change in Chosen’s public API or a change in behavior.

I think therefore the tests should be optimized for readability of first-time contributors, serving as an assurance that Chosen behaves exactly the way you expect. Executable documentation, if you will.

I agree with your thoughts though, it’s gross to write two copies of nearly identical tests, and I’m definitely interested in a way to make that better.

@Mikk3lRo
Copy link
Contributor Author

Only having one set of tests and having that set be as readable as possible is likely to improve the chance that contributors actually write tests for future features.

I do see the problem with passing through "layers" in tests, but I can't think of a way to eliminate the duplicate tests without it. At the same time the wrapper functions ensures that common things are done the same way by all tests and provides a single place to change it, should that ever become relevant.

Not to mention that a lot of people (myself included) has to learn prototype to be able to contribute at all (The number of SO questions says it all: jquery-chosen: 1115, prototype-chosen: 13). Which is why I wrote the "framework wrapper" with syntax almost equal to jQuery.

Should I update the PR to not include the HTML-generating helper? IMO that is the main thing "blurring the picture", the rest definitely improves readability - for newcomers and long-time contributors alike...

-For the record I do understand if this is never merged even if you agree with everything else... I am aware that it clashes with every other open PR that touches the tests - though they can easily be adapted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants