Pull requests are welcome! Here are specific contributions we're looking for:
-
Add more assertions. Quixote can test any CSS property using the
QElement.getRawStyle()
method, but the built-in assertions are more sophisticated. So far, Quixote's assertions have focused on layout, but We'd like to have assertions for every aspect of page rendering. To learn how to add an assertion, see the Architecture section below. Please open an issue before starting work so we can discuss the API design. -
Start the website. I've set up quixote-css.com for documentation, but so far, it's just a placeholder. The source code is in docs.
-
Create a logo. I'm imagining Don Quixote jousting with a CSS windmill, but feel free to let your imagination run wild.
-
Let us know how Quixote works for you. Download the code, try it out, and let me know how it worked for you. Create an issue if anything didn't work smoothly or if you had trouble understanding something.
-
Tell your friends and colleagues. Even if you can't contribute anything, spreading the word is a big help. Let people know about Quixote and why you like it.
If you're planning on making a pull request, here's a helpful checklist:
-
Create an issue so we know what you're working on and other people who are interested can coordinate with you.
-
Fork the Quixote repository on GitHub.
-
Set up your computer by following the "Working with the Code" instructions below.
-
Create a branch for your changes. Start from the
master
branch. It has the latest known-good code.$ git checkout master $ git checkout -b <your_branch>
-
Make your changes, committing as desired. When you're done, make sure the tests pass.
-
Push your changes to GitHub:
git push
. -
Create a pull request and add a comment referencing the issue it addresses.
Thank you!
To work with the code on your own computer:
- Install Node.js.
- Clone the GitHub repository:
git clone https://github.com/jamesshore/quixote.git
- All commands must run from the root of the source tree:
cd quixote
. - Run the tests as described below. They should pass. If not, open an issue or contact me @jamesshore on Twitter.
- Run
./jake.sh karma
to start the Karma server. - Start the browsers you want to test and point each one at
http://localhost:9876
. - Run
./jake.sh loose=true
to build and test, or./watch.js loose=true
to automatically rebuild when you make a change. You can use./jake.sh quick loose=true
(or./watch.js quick loose=true
) to only build files that have changed.
If you get a timeout error in __reset.js
, it's probably due to the window being hidden or a different tab being shown. Many browsers deprioritize tabs that aren't visible, which causes the tests to timeout. To fix the issue, make sure some portion of the Karma page is visible.
At this time, the build has only been tested on Mac OS X. It should also work on Unix without any trouble. It's theoretically capable of working on Windows, but needs some script work before that's easy and convenient.
To look at the results of a test run, use this process:
- Change the test you want to debug to use
it.only(...)
. - Run the tests using the
quick
option so the build only runs the tests you changed. - Press the 'Debug' button on the Karma page in the browser you want to check.
- Karma will open the test in a new tab and you'll be able to see the output.
- To check again, run the tests again and reload Karma's debug tab.
You can pass the following options to ./jake.sh
and ./watch.js
:
-
-T
displays available build targets. -
loose=true
prevents the build from failing if you don't test every browser or if your Node version isn't a perfect match. -
capture=Firefox,Safari,etc
automatically launches, uses, and quits the requested browsers. You can use this instead of running./jake.sh karma
and manually starting the browsers yourself. It's most useful for automated build runners such as Travis CI. Note that you may need to install the appropriate launchers; e.g.,npm install karma-safari-launcher
.
You won't need to run these scripts, but in case you're curious:
./integrate.sh
validates the dev branch and merges it into the known-good master branch../release.sh
publishes to npm, github, and our documentation site.
To run tests against mobile browsers, you can either use a real mobile device or you can use a simulator. Either way, once you have the mobile OS running, the process is the same as it is on your desktop: start the web browser you want to test, then point it at the Karma server running on your computer.
But, instead of loading http://localhost:9876
, you'll need to substitute the name or IP address of your computer, such as http://192.168.0.1:9876
. You'll also need to make sure there's no firewall running that prevents the mobile device from talking to your computer.
- You'll need a Mac running a recent version of MacOS.
- Install XCode from the App Store (it's free).
- Select the "XCode" menu item and choose Open Developer Tool --> Simulator.
- The simulated iOS device will boot and can be manipulated normally. The window can be moved and resized by dragging the bezel.
You can perform this one-time process to make it easier to run the Simulator:
- Locate XCode.app in the Finder. It's probably in the Applications folder.
- Right-click XCode.app and select "Show Package Contents".
- Navigate to Contents/Developer/Applications.
- You should see Simulator.app. Right-click it and select "Make Alias".
- Drag the alias to the Applications folder. You might find it useful to rename it to "iOS Simulator.app".
- From now on, you should be able to run the Simulator by opening the alias in the Applications folder.
(These instructions were created using XCode v11.3.1.)
- Install Android Studio. During the install, select the "Performance" and "Android Virtual Device" options. If you already have Android Studio installed, see these instructions for installing the emulator.
- After installing, run Android Studio and choose "Start a new Android Studio project," then "Empty Activity." Use the default configuration and press "Finish."
- After the IDE opens, find and press the "AVD Manager" icon below the title bar. It looks like a small phone with a green blob in the lower right corner. It may also be available in the Tools menu.
- A window labelled "Your Virtual Devices" will appear. If you see a link labelled "Update System Images," select it and perform the installation.
- In the "Your Virtual Devices" window, if you don't see the version of Android you want, press the "Create Virtual Device" button.
- Once you have the device you want, press the small green "play" button on the right.
- The emulated Android device will boot and can be manipulated normally. The window can be moved by dragging the bezel. It can be resized by dragging one of the corners.
(These instructions were created using Android Studio v3.6.2.)
All the Quixote source and unit test code is in src
. Test code starts with an underscore. The src
directory uses the following structure:
src
contains our top-level API.src/descriptors
contains descriptors: objects that describe how a CSS value can be calculated and displayed. (See the Architecture section below for more details.)src/values
contains values: objects that represent a calculated result.src/util
has a few helper modules.
Other top-level directories contain infrastructure and support.
build
contains build scripts and configuration.docs
contains API docs and the placeholder website (online at quixote-css.com).dist
contains the compiled library.spikes
contains one-off experiments.test
contains end-to-end tests.node_modules
contains third-party libraries needed for the build scripts.vendor
contains third-party libraries needed for Quixote itself.
release
is for releases. It's the default branch shown on GitHub.master
is the known-good integration branch. This branch should always build and pass its tests. When creating a pull request, start frommaster
.dev
contains work in progress. This is just for the project maintainer.
Previous commits on the integration branch have "INTEGRATE" in their commit comment.
If you aren't familiar with the Quixote API, take a moment to review the documentation for the QElement
class, particularly the "Properties" section.
Quixote's architecture revolves around two concepts:
- Descriptors, which provide assertions and compute values. They're things like
top edge of .navbar
. - Values, which perform calculations and provide explanations. They're things like
50px
.
When you make an assertion with Quixote, you run a line of code that looks like this:
navbar.top.should.equal(header.bottom);
In this example, navbar.top
and header.bottom
are both Descriptors. Specifically, they're ElementEdge
descriptors (a type of PositionDescriptor
). ElementEdge
descriptors represent the edge of an element.
Here's what the should.equal()
function does when it's called:
should.equal()
asksnavbar.top
to calculate its Value object.navbar.top
usesQElement.getRawPosition()
andQElement.getRawScrollPosition()
to find the top edge of thenavbar
element, which is63
in this example.navbar.top
creates aPosition
value object with the value of63
and returns it.
should.equal()
asksheader.bottom
to calculate its Value object.header.bottom
usesQElement.getRawPosition()
andQElement.getRawScrollPosition()
to find the bottom edge of theheader
element, which is50
in this example.header.bottom
creates aPosition
value object with the value of50
and returns it.
should.equal()
asks the twoPosition
objects if they're equal.- The Position objects check their values. One is
63
. The other one is50
.
- The Position objects check their values. One is
If the two Value objects were equal, that would be the end. The function would return and the assertion would pass.
In this example, though, they're not equal. The assertion will throw an exception with this error:
top edge of '.navbar' should be 13px higher.
Expected: 50px (bottom edge of '#header')
But was: 63px
Here's how that error is generated:
- Line one:
should.equal()
asks thenavbar.top
to convert itself to a string. It saystop edge of '.navbar'
.should.equal()
asks the expectedPosition
(the one that's50
) how it's different than the actualPosition
(the one that's63
). It says13px higher
.should.equal()
concatenates these answers into the first line of the error:top edge of '.navbar'
should be
13px higher
.
- Line two:
should.equal()
asks the expectedPosition
to convert itself to a string. It says50px
.should.equal()
asksheader.bottom
to convert itself to a string. It saysbottom edge of '#header'
.should.equal()
concatenates these answers into the second line of the error:Expected:
50px
(bottom edge of '#header')
- Line three:
should.equal()
asks the actualPosition
to convert itsel to a string. It says63px
.should.equal()
concatenates this answer into the third line of the error:But was:
63px
.
Or, to put it differently:
- The Descriptor objects handle what was tested:
top edge of '.navbar'
andbottom edge of #header
. - The Value objects handle the results:
13px higher
,50px
, and63px
. - The assertion handles the scaffolding:
should be
,Expected:
, andBut was:
.
To add new properties and assertions, start by opening an issue so we can discuss the API. Once that's done, you'll need to perform these steps:
- Decide on a property and assertion API. For example,
element.backgroundColor.should.equal('#ff0000')
- Create a new Value class that can represent those sort of values. For example,
Color
. - Create a new Descriptor class that can calculate the value of the property. For example,
BackgroundColor
. You can also add custom assertions, such aselement.backgroundColor.should.beDarkerThan()
. - Modify an existing class to include your descriptor. For example, add
element.backgroundColor
toQElement
. - Update API documentation.
Your Value class will be responsible for parsing strings, displaying strings, and comparing values.
Your Descriptor class will be responsible for calculating values and making assertions.
For detailed instructions, see our tutorials:
- Indent with tabs, not spaces. Don't use tabs for anything other than indentation.
- Use factory methods, not constructors. In other words, use
Size.create()
, notnew Size()
. - All code must be compatible with IE 8. That means no ES6 syntax.
- Use test-driven development to ensure that your code is thoroughly tested and all edge cases considered.
For use by the project maintainer.
- Update documentation as appropriate.
- Ensure all changes are checked in.
- Run
./integrate.sh
. - If the script fails due to new distribution files, check them in with the comment "Update distribution files", then run
./integrate.sh
again. - Consider releasing to GitHub with
./release github
.
- Start from clean (integrated) dev branch.
- Merge pull request into dev branch using
git pull
line listed under the "command line instructions" link at the bottom of GitHub's pull request conversation thread. - Revise and make additional commits as needed, including updating documentation.
- Credit contributor at bottom of README.md.
- Integrate the dev branch as described above.
- Close pull request. Include a comment saying which version will include the result. Tag associated issue as 'done'.
- Review Github for 'ready to integrate' issues and pull requests and consider integrating them.
- Remove any temporary branches (list with
git branch
, delete withgit branch -d <name>
). - Review and update readme and API documentation as needed.
- Update changelog and roadmap.
- Integrate as described above.
- Run
./release [major | minor | patch]
to release. The release script releases to npm and github. - Close issues tagged 'done' that were released.
- Publicize.