diff --git a/core/lib/Thelia/Tests/Functionnal/casperjs/conf/dit.js b/core/lib/Thelia/Tests/Functionnal/casperjs/conf/dit.js
new file mode 100644
index 000000000..b89267246
--- /dev/null
+++ b/core/lib/Thelia/Tests/Functionnal/casperjs/conf/dit.js
@@ -0,0 +1,3 @@
+//DIT = Environnement d'intégration
+var copark_base_url = 'http://ws.copark.dit.linux.thelia.fr/';
+casper.test.done(0);
diff --git a/core/lib/Thelia/Tests/Functionnal/casperjs/conf/local.js b/core/lib/Thelia/Tests/Functionnal/casperjs/conf/local.js
new file mode 100644
index 000000000..366d297f2
--- /dev/null
+++ b/core/lib/Thelia/Tests/Functionnal/casperjs/conf/local.js
@@ -0,0 +1,3 @@
+//LOCAL = ton pc
+var thelia2_base_url = 'http://www.thelia2.dev/index.php/';
+casper.test.done(0);
diff --git a/core/lib/Thelia/Tests/Functionnal/casperjs/conf/uat.js b/core/lib/Thelia/Tests/Functionnal/casperjs/conf/uat.js
new file mode 100644
index 000000000..02b753869
--- /dev/null
+++ b/core/lib/Thelia/Tests/Functionnal/casperjs/conf/uat.js
@@ -0,0 +1,3 @@
+//UAT = Recette client
+var copark_base_url = 'http://ws.copark.uat.openstudio.fr/';
+casper.test.done(0);
\ No newline at end of file
diff --git a/core/lib/Thelia/Tests/Functionnal/casperjs/exe/00_parameters.js b/core/lib/Thelia/Tests/Functionnal/casperjs/exe/00_parameters.js
new file mode 100644
index 000000000..81b980d41
--- /dev/null
+++ b/core/lib/Thelia/Tests/Functionnal/casperjs/exe/00_parameters.js
@@ -0,0 +1,12 @@
+
+casper.test.comment('Please edit 00_parameters.js to add your configuration');
+
+var thelia2_login_admin_url = thelia2_base_url + 'admin/login';
+
+
+//var findMyId = /([0-9]+)$/;
+//var currentId;
+
+casper.test.comment('Variables are set');
+
+casper.test.done(0);
diff --git a/core/lib/Thelia/Tests/Functionnal/casperjs/exe/10_login.js b/core/lib/Thelia/Tests/Functionnal/casperjs/exe/10_login.js
new file mode 100644
index 000000000..c9c4095db
--- /dev/null
+++ b/core/lib/Thelia/Tests/Functionnal/casperjs/exe/10_login.js
@@ -0,0 +1,25 @@
+casper.test.comment('Testing login');
+
+casper.start(thelia2_login_admin_url, function() {
+ this.echo('\nLOGIN');
+ this.test.assertTitle('Welcome - Thelia Back Office', 'Web page title OK');
+ this.sendKeys('form[action*="checklogin"] input[name="thelia_admin_login[username]"]', 'thelia2');
+ this.sendKeys('form[action*="checklogin"] input[name="thelia_admin_login[password]"]', 'thelia2');
+ this.click('form[action*="checklogin"] input[type="submit"]');
+});
+
+casper.wait(1000, function() {
+ this.echo("\nWaiting....");
+});
+
+casper.then(function(){
+ this.echo('\nDASHBOARD');
+ console.log('Now on : ' + this.getCurrentUrl());
+ this.test.assertTitle('Back-office home - Thelia Back Office', 'Web page title OK');
+ this.test.assertSelectorHasText('#wrapper > div', ' This is the administration home page. Put some interesting statistics here, and display useful information :) ', 'Web page main content OK');
+});
+
+//RUN
+casper.run(function() {
+ this.test.done();
+});
\ No newline at end of file
diff --git a/core/lib/Thelia/Tests/Functionnal/casperjs/run.sh b/core/lib/Thelia/Tests/Functionnal/casperjs/run.sh
new file mode 100755
index 000000000..6e054b152
--- /dev/null
+++ b/core/lib/Thelia/Tests/Functionnal/casperjs/run.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+# @author Guillaume MOREL
+
+echo "Force dropping database. All data will be lost."
+
+cd local/config/
+
+echo -e "\n\e[01;34m[INFO] Building Models file\e[00m\n"
+../../bin/propel build -v --output-dir=../../core/lib/
+
+echo -e "\n\e[01;34m[INFO] Building SQL CREATE file\e[00m\n"
+../../bin/propel sql:build -v --output-dir=../../install/
+
+echo -e "\n\e[01;34m[INFO] Reloaded Thelia2 database\e[00m\n"
+cd ../..
+rm install/sqldb.map
+php Thelia thelia:dev:reloadDB
+
+echo -e "\n\e[01;34m[INFO] Installing fixtures\e[00m\n"
+php install/faker.php
+
+echo -e "\n\e[01;34m[INFO] Adding admin\e[00m\n"
+php Thelia thelia:create-admin --login_name thelia2 --password thelia2 --last_name thelia2 --first_name thelia2
+
+# casperjs test ./exe --pre=./conf/local.js --direct
+
diff --git a/core/lib/Thelia/Tests/Functionnal/casperjs/run_ci.sh b/core/lib/Thelia/Tests/Functionnal/casperjs/run_ci.sh
new file mode 100755
index 000000000..785d8ee49
--- /dev/null
+++ b/core/lib/Thelia/Tests/Functionnal/casperjs/run_ci.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+# @author Quentin Dufour
+
+/usrcasperjs test ./exe --pre=./conf/dit.js --xunit=reports/report.xml
diff --git a/templates/admin/default/assets/js/coupon.js b/templates/admin/default/assets/js/coupon.js
new file mode 100644
index 000000000..5c52aa097
--- /dev/null
+++ b/templates/admin/default/assets/js/coupon.js
@@ -0,0 +1,148 @@
+$(function($){
+
+ // Clean array from deleteValue (undefined) keys
+ Array.prototype.clean = function(deleteValue) {
+ for (var i = 0; i < this.length; i++) {
+ if (this[i] == deleteValue) {
+ this.splice(i, 1);
+ i--;
+ }
+ }
+ return this;
+ };
+
+ // Remove 1 Rule then Save Rules AJAX
+ couponManager.removeRuleAjax = function(id) {
+ // Delete rule in temporary array
+ delete couponManager.rulesToSave[id];
+ couponManager.rulesToSave.clean(undefined);
+
+ // Save
+ couponManager.saveRuleAjax();
+ };
+
+ // Add 1 Rule / or update the temporary Rules array then Save Rules via AJAX
+ couponManager.addRuleAjax = function(id) {
+ console.log('addRuleAjax '+ id);
+ // If create
+ if(!id) {
+ console.log('pushing');
+ couponManager.rulesToSave.push(couponManager.ruleToSave);
+ } else { // else update
+ console.log('editing ' + id);
+ couponManager.rulesToSave[id] = couponManager.ruleToSave;
+ // reset edit mode to off
+ couponManager.ruleIdToUpdate = false;
+ }
+
+ // Save
+ couponManager.saveRuleAjax();
+ };
+
+ // Set rule inputs to allow editing
+ couponManager.updateRuleAjax = function(id) {
+ couponManager.ruleToUpdate = couponManager.rulesToSave[id];
+ console.log('Set id to edit to ' + id);
+ couponManager.ruleIdToUpdate = id;
+
+ // Deleting this rule, we will reset it
+ delete couponManager.rulesToSave[id];
+
+ // Set the rule selector
+ $("#category-rule option").filter(function() {
+ return $(this).val() == couponManager.ruleToUpdate.serviceId;
+ }).prop('selected', true);
+
+ // Force rule input refresh
+ couponManager.loadRuleInputs(couponManager.ruleToUpdate.serviceId, function() {
+ couponManager.fillInRuleInputs();
+ });
+ };
+
+ // Fill in rule inputs
+ couponManager.fillInRuleInputs = function() {
+ console.log('fillInRuleInputs with');
+ console.log(couponManager.ruleToUpdate);
+ var operatorId = null;
+ var valueId = null;
+ var idName = null;
+
+ for (idName in couponManager.ruleToUpdate.operators) {
+ // Setting idName operator select
+ operatorId = idName + '-operator';
+ $('#' + operatorId).val(couponManager.ruleToUpdate.operators[idName]);
+
+ valueId = idName + '-value';
+ // Setting idName value input
+ $('#' + valueId).val(couponManager.ruleToUpdate.values[idName]);
+ }
+ couponManager.ruleToSave = couponManager.ruleToUpdate;
+
+ var id = couponManager.ruleIdToUpdate;
+ console.log('id to edit = ' + id);
+ if(id) {
+ console.log('setint rulesToSave[' + id + ']');
+ console.log(couponManager.ruleToSave);
+ couponManager.rulesToSave[id] = couponManager.ruleToSave;
+ }
+ };
+
+ // Save rules on click
+ couponManager.onClickSaveRule = function() {
+ $('#constraint-save-btn').on('click', function () {
+ couponManager.addRuleAjax(couponManager.ruleIdToUpdate);
+ });
+ };
+ couponManager.onClickSaveRule();
+
+ // Remove rule on click
+ couponManager.onClickDeleteRule = function() {
+ $('.constraint-delete-btn').on('click', function (e) {
+ e.preventDefault();
+ var $this = $(this);
+ couponManager.removeRuleAjax($this.attr('data-int'));
+ });
+ };
+ couponManager.onClickDeleteRule();
+
+ // Update rule on click
+ couponManager.onClickUpdateRule = function() {
+ $('.constraint-update-btn').on('click', function (e) {
+ e.preventDefault();
+ var $this = $(this);
+ couponManager.updateRuleAjax($this.attr('data-int'));
+
+ // Hide row being updated
+ $this.parent().parent().remove();
+ });
+ };
+ couponManager.onClickUpdateRule();
+
+ // Reload effect inputs when changing effect
+ couponManager.onEffectChange = function() {
+ $('#effect').on('change', function () {
+ var optionSelected = $("option:selected", this);
+ $('#effectToolTip').html(optionSelected.attr("data-description"));
+ });
+ };
+ couponManager.onEffectChange();
+
+ // Reload rule inputs when changing effect
+ couponManager.onRuleChange = function() {
+ $('#category-rule').on('change', function () {
+ couponManager.loadRuleInputs($(this).val(), function(ruleToSave) {});
+ });
+ };
+ couponManager.onRuleChange();
+
+ // Fill in ready to be saved rule array
+ // var onInputsChange = function()
+ // In AJAX response
+
+});
+
+// Rule to save
+
+var couponManager = {};
+couponManager.ruleToSave = {};
+couponManager.ruleIdToUpdate = false;
\ No newline at end of file
diff --git a/templates/admin/default/coupon-update.html b/templates/admin/default/coupon-update.html
index 3895475b4..925296aae 100755
--- a/templates/admin/default/coupon-update.html
+++ b/templates/admin/default/coupon-update.html
@@ -37,24 +37,19 @@
{/javascripts}
+ {javascripts file='assets/js/coupon.js'}
+
+ {/javascripts}
+
{/block}
diff --git a/templates/admin/default/coupon/rule-input-ajax.html b/templates/admin/default/coupon/rule-input-ajax.html
index 4933c6745..cdf683d21 100644
--- a/templates/admin/default/coupon/rule-input-ajax.html
+++ b/templates/admin/default/coupon/rule-input-ajax.html
@@ -72,29 +72,31 @@
\ No newline at end of file