Merge branch 'clear-authorship-color-test-spec' of git://github.com/johnyma22/etherpad-lite into feature/frontend-tests

pull/1097/merge
Peter 'Pita' Martischka 2012-10-27 16:33:34 +01:00
commit a2a208a95b
10 changed files with 302 additions and 127 deletions

126
README.md
View File

@ -1,126 +0,0 @@
# Making collaborative editing the standard on the web
# About
Etherpad lite is a really-real time collaborative editor spawned from the Hell fire of Etherpad.
We're reusing the well tested Etherpad easysync library to make it really realtime. Etherpad Lite
is based on node.js ergo is much lighter and more stable than the original Etherpad. Our hope
is that this will encourage more users to use and install a realtime collaborative editor. A smaller, manageable and well
documented codebase makes it easier for developers to improve the code and contribute towards the project.
**Etherpad vs Etherpad lite**
<table>
<tr>
<td>&nbsp;</td><td><b>Etherpad</b></td><td><b>Etherpad Lite</b></td>
</tr>
<tr>
<td align="right">Size of the folder (without git history)</td><td>30 MB</td><td>1.5 MB</td>
</tr>
<tr>
<td align="right">Languages used server side</td><td>Javascript (Rhino), Java, Scala</td><td>Javascript (node.js)</td>
</tr>
<tr>
<td align="right">Lines of server side Javascript code</td><td>~101k</td><td>~9k</td>
</tr>
<tr>
<td align="right">RAM Usage immediately after start</td><td>257 MB (grows to ~1GB)</td><td>16 MB (grows to ~30MB)</td>
</tr>
</table>
Etherpad Lite is designed to be easily embeddable and provides a [HTTP API](https://github.com/Pita/etherpad-lite/wiki/HTTP-API)
that allows your web application to manage pads, users and groups. It is recommended to use the client implementations available for this API, listed on [this wiki page](https://github.com/Pita/etherpad-lite/wiki/HTTP-API-client-libraries).
There is also a [jQuery plugin](https://github.com/johnyma22/etherpad-lite-jquery-plugin) that helps you to embed Pads into your website
**Visit [beta.etherpad.org](http://beta.etherpad.org) to test it live**
Also, check out the **[FAQ](https://github.com/Pita/etherpad-lite/wiki/FAQ)**, really!
# Installation
## Windows
### Prebuilt windows package
This package works out of the box on any windows machine, but it's not very useful for developing purposes...
1. Download the windows package <https://github.com/Pita/etherpad-lite/downloads>
2. Extract the folder
Now, run `start.bat` and open <http://localhost:9001> in your browser. You like it? [Next steps](#next-steps).
### Fancy install
You'll need [node.js](http://nodejs.org) and (optionally, though recommended) git.
1. Grab the source, either
- download <https://github.com/Pita/etherpad-lite/zipball/master>
- or `git clone https://github.com/Pita/etherpad-lite.git` (for this you need git, obviously)
2. start `bin\installOnWindows.bat`
Now, run `start.bat` and open <http://localhost:9001> in your browser.
Update to the latest version with `git pull origin`, then run `bin\installOnWindows.bat`, again.
[Next steps](#next-steps).
## Linux
You'll need gzip, git, curl, libssl develop libraries, python and gcc.
*For Debian/Ubuntu*: `apt-get install gzip git-core curl python libssl-dev pkg-config build-essential`
*For Fedora/CentOS*: `yum install gzip git-core curl python openssl-devel && yum groupinstall "Development Tools"`
Additionally, you'll need [node.js](http://nodejs.org).
**As any user (we recommend creating a separate user called etherpad-lite):**
1. Move to a folder where you want to install Etherpad Lite. Clone the git repository `git clone git://github.com/Pita/etherpad-lite.git`
2. Change into the new directory containing the cloned source code `cd etherpad-lite`
Now, run `bin\run.sh` and open <http://127.0.0.1:9001> in your browser.
Update to the latest version with `git pull origin`. The next start with bin/run.sh will update the dependencies.
You like it? [Next steps](#next-steps).
# Next Steps
## Tweak the settings
You can modify the settings in `settings.json`. (If you need to handle multiple settings files, you can pass the path to a settings file to `bin/run.sh` using the `-s|--settings` option. This allows you to run multiple Etherpad Lite instances from the same installation.)
You should use a dedicated database such as "mysql", if you are planning on using etherpad-lite in a production environment, since the "dirtyDB" database driver is only for testing and/or development purposes.
## Helpful resources
The [wiki](https://github.com/Pita/etherpad-lite/wiki) is your one-stop resource for Tutorials and How-to's, really check it out! Also, feel free to improve these wiki pages.
Documentation can be found in `docs/`.
# Development
## Things you should know
Read this [git guide](http://learn.github.com/p/intro.html) and watch this [video on getting started with Etherpad Lite Development](http://youtu.be/67-Q26YH97E).
If you're new to node.js, start with Ryan Dahl's [Introduction to Node.js](http://youtu.be/jo_B4LTHi3I).
You can debug Etherpad lite using `bin/debugRun.sh`.
If you want to find out how Etherpad's `Easysync` works (the library that makes it really realtime), start with this [PDF](https://github.com/Pita/etherpad-lite/raw/master/doc/easysync/easysync-full-description.pdf) (complex, but worth reading).
## Getting started
You know all this and just want to know how you can help?
Look at the [TODO list](https://github.com/Pita/etherpad-lite/wiki/TODO) and our [Issue tracker](https://github.com/Pita/etherpad-lite/issues). (Please consider using [jshint](http://www.jshint.com/about/), if you plan to contribute code.)
Also, and most importantly, read our [**Developer Guidelines**](https://github.com/Pita/etherpad-lite/wiki/Developer-Guidelines), really!
# Get in touch
Join the [mailinglist](http://groups.google.com/group/etherpad-lite-dev) and make some noise on our freenode irc channel [#etherpad-lite-dev](http://webchat.freenode.net?channels=#etherpad-lite-dev)!
# Modules created for this project
* [ueberDB](https://github.com/Pita/ueberDB) "transforms every database into a object key value store" - manages all database access
* [channels](https://github.com/Pita/channels) "Event channels in node.js" - ensures that ueberDB operations are atomic and in series for each key
* [async-stacktrace](https://github.com/Pita/async-stacktrace) "Improves node.js stacktraces and makes it easier to handle errors"
# Donate!
* [Flattr] (http://flattr.com/thing/71378/Etherpad-Foundation)
* Paypal - Press the donate button on [etherpad.org](http://etherpad.org)
# License
[Apache License v2](http://www.apache.org/licenses/LICENSE-2.0.html)

View File

@ -18,7 +18,11 @@
<script src="lib/jquery.iframe.js"></script>
<script src="helper.js"></script>
<!--
<script src="specs/helper.js"></script>
-->
<script src="specs/button_undo.js"></script>
<!--
<script src="specs/button_bold.js"></script>
<script src="specs/button_italic.js"></script>
<script src="specs/keystroke_urls_become_clickable.js"></script>
@ -27,6 +31,6 @@
<script src="specs/embed_value.js"></script>
<script src="specs/keystroke_urls_become_clickable.js"></script>
<script src="specs/button_indentation.js"></script>
-->
<script src="runner.js"></script>
</html>

View File

@ -0,0 +1,52 @@
describe("clear authorship colors button", function(){
//create a new pad before each test run
beforeEach(function(cb){
helper.newPad(cb);
this.timeout(5000);
});
it("makes text clear authorship colors", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
// override the confirm dialogue functioon
helper.padChrome$.window.confirm = function(){
return true;
}
//get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first();
// Get the original text
var originalText = inner$("div").first().text();
// Set some new text
var sentText = "Hello";
//select this text element
$firstTextElement.sendkeys(sentText);
helper.waitFor(function(){
return inner$("div").first().text() === sentText + originalText; // wait until we have the full value available
}).done(function(){
// does the first divs span include an author class?
var hasAuthorClass = inner$("div span").first().attr("class").indexOf("author") !== -1;
expect(hasAuthorClass).to.be(true);
//get the clear authorship colors button and click it
var $clearauthorshipcolorsButton = chrome$(".buttonicon-clearauthorship");
$clearauthorshipcolorsButton.click();
// does the first divs span include an author class?
var hasAuthorClass = inner$("div span").first().attr("class").indexOf("author") !== -1;
expect(hasAuthorClass).to.be(false);
// does the first div include an author class?
var hasAuthorClass = inner$("div").first().attr("class").indexOf("author") !== -1;
expect(hasAuthorClass).to.be(false);
done();
});
});
});

View File

@ -0,0 +1,47 @@
describe("assign ordered list", function(){
//create a new pad before each test run
beforeEach(function(cb){
helper.newPad(cb);
this.timeout(5000);
});
it("insert ordered list text", function(done){
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var $insertorderedlistButton = chrome$(".buttonicon-insertorderedlist");
$insertorderedlistButton.click();
helper.waitFor(function(){
return inner$("div").first().find("ol li").length === 1;
}).done(done);
});
it("keeps the numbered list on enter for the new line - EMULATES PASTING INTO A PAD", function(done){
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
var $insertorderedlistButton = chrome$(".buttonicon-insertorderedlist");
$insertorderedlistButton.click();
//type a bit, make a line break and type again
var $firstTextElement = inner$("div span").first();
$firstTextElement.sendkeys('line 1');
$firstTextElement.sendkeys('{enter}');
$firstTextElement.sendkeys('line 2');
$firstTextElement.sendkeys('{enter}');
helper.waitFor(function(){
return inner$("div span").first().text().indexOf("line 2") === -1;
}).done(function(){
var $newSecondLine = inner$("div").first().next();
var hasOLElement = $newSecondLine.find("ol li").length === 1;
console.log($newSecondLine.find("ol"));
expect(hasOLElement).to.be(true);
expect($newSecondLine.text()).to.be("line 2");
var hasLineNumber = $newSecondLine.find("ol").attr("start") === 2;
expect(hasLineNumber).to.be(true); // This doesn't work because pasting in content doesn't work
done();
});
});
});

View File

@ -0,0 +1,35 @@
describe("undo button", function(){
beforeEach(function(cb){
helper.newPad(cb); // creates a new pad
this.timeout(5000);
});
it("undo some typing", function(done){
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
// get the first text element inside the editable space
var $firstTextElement = inner$("div span").first();
var originalValue = $firstTextElement.text(); // get the original value
$firstTextElement.sendkeys("foo"); // send line 1 to the pad
var modifiedValue = $firstTextElement.text(); // get the modified value
expect(modifiedValue).not.to.be(originalValue); // expect the value to change
// get clear authorship button as a variable
var $undoButton = chrome$(".buttonicon-undo");
var $redoButton = chrome$(".buttonicon-redo");
// click the buttons
$undoButton.click();
$redoButton.click();
helper.waitFor(function(){
return inner$("div span").first().text() === originalValue;
}).done(function(){
var finalValue = inner$("div span").first().text();
expect(finalValue).to.be(modifiedValue); // expect the value to change
done();
});
});
});

View File

@ -0,0 +1,36 @@
describe("strikethrough button", function(){
//create a new pad before each test run
beforeEach(function(cb){
helper.newPad(cb);
this.timeout(5000);
});
it("makes text strikethrough", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first();
//select this text element
$firstTextElement.sendkeys('{selectall}');
//get the strikethrough button and click it
var $strikethroughButton = chrome$(".buttonicon-strikethrough");
$strikethroughButton.click();
//ace creates a new dom element when you press a button, so just get the first text element again
var $newFirstTextElement = inner$("div").first();
// is there a <i> element now?
var isstrikethrough = $newFirstTextElement.find("s").length === 1;
//expect it to be strikethrough
expect(isstrikethrough).to.be(true);
//make sure the text hasn't changed
expect($newFirstTextElement.text()).to.eql($firstTextElement.text());
done();
});
});

View File

@ -0,0 +1,33 @@
describe("undo button", function(){
beforeEach(function(cb){
helper.newPad(cb); // creates a new pad
this.timeout(5000);
});
it("undo some typing", function(done){
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
// get the first text element inside the editable space
var $firstTextElement = inner$("div span").first();
var originalValue = $firstTextElement.text(); // get the original value
$firstTextElement.sendkeys("foo"); // send line 1 to the pad
var modifiedValue = $firstTextElement.text(); // get the modified value
expect(modifiedValue).not.to.be(originalValue); // expect the value to change
// get clear authorship button as a variable
var $undoButton = chrome$(".buttonicon-undo");
// click the button
$undoButton.click();
helper.waitFor(function(){
return inner$("div span").first().text() === originalValue;
}).done(function(){
var finalValue = inner$("div span").first().text();
expect(finalValue).to.be(originalValue); // expect the value to change
done();
});
});
});

View File

@ -0,0 +1,31 @@
describe("change username value", function(){
//create a new pad before each test run
beforeEach(function(cb){
helper.newPad(cb);
this.timeout(5000);
});
it("makes sure changing username works", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
//click on the settings button to make settings visible
var $userButton = chrome$(".buttonicon-showusers");
$userButton.click();
var $usernameInput = chrome$("#myusernameedit");
$usernameInput.click();
$usernameInput.sendkeys('{selectall}');
$usernameInput.sendkeys('{del}');
$usernameInput.sendkeys('John McLear');
$usernameInput.sendkeys('{enter}');
var correctUsernameValue = $usernameInput.val() === "John McLear";
//check if the username has been changed to John McLear
expect(correctUsernameValue).to.be(true);
done();
});
});

View File

@ -0,0 +1,29 @@
describe("chat always ons creen select", function(){
//create a new pad before each test run
beforeEach(function(cb){
helper.newPad(cb);
this.timeout(5000);
});
it("makes chat stick to right side of the screen", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
//click on the settings button to make settings visible
var $settingsButton = chrome$(".buttonicon-settings");
$settingsButton.click();
//get the chat selector
var $stickychatCheckbox = chrome$("#options-stickychat");
//select monospace and fire change event
$stickychatCheckbox.attr('selected','selected');
$stickychatCheckbox.change();
//check if chat changed to get the stickychat Class
var hasStickyChatClass = chrome$(".chatbox").hasClass("stickychat");
expect(hasStickyChatClass).to.be(true);
done();
});
});

View File

@ -0,0 +1,34 @@
describe("enter keystroke", function(){
//create a new pad before each test run
beforeEach(function(cb){
helper.newPad(cb);
this.timeout(5000);
});
it("creates a enw line & puts cursor onto a new line", function(done) {
var inner$ = helper.padInner$;
var chrome$ = helper.padChrome$;
//get the first text element out of the inner iframe
var $firstTextElement = inner$("div").first();
// get the original string value minus the last char
var originalTextValue = $firstTextElement.text();
// simulate key presses to enter content
$firstTextElement.sendkeys('{enter}');
//ace creates a new dom element when you press a keystroke, so just get the first text element again
var $newFirstTextElement = inner$("div").first();
helper.waitFor(function(){
return inner$("div").first().text() === "";
}).done(function(){
var $newSecondLine = inner$("div").first().next();
var newFirstTextElementValue = inner$("div").first().text();
expect(newFirstTextElementValue).to.be(""); // expect the first line to be blank
expect($newSecondLine.text()).to.be(originalTextValue); // expect the second line to be the same as the original first line.
done();
});
});
});