diff --git a/API.md b/API.md deleted file mode 100644 index 0bfa18fb7517a9a0fb6a27622c1e0db7c54b3b6b..0000000000000000000000000000000000000000 --- a/API.md +++ /dev/null @@ -1,287 +0,0 @@ -API: Client / Server -==================== - - -Form initial call ------------------ - -Request: index.php (QuickFormQuery.php, included by Typo3 extension) - -Response: -Form attributes: - -data-hidden: 'yes'|'no' - yes: The element is not visible yet, maybe later. -data-disabled: 'yes'|'no' - yes: The element is visible, but the user can't interact with it. -data-required: 'yes'|'no' - yes: The element is required. The form can't be submitted if any required element is empty. - -## General - - -Asynchronous request (read AJAX) initiated by the client receive a JSON Response from the server containing at least: - - { - "status": "success"|"error", - "message": "<message>" - } - -`status` indicates whether or not the request has been fullfiled by the server (`"success"`) or encountered an error (`"error"`). -On `"error"` the client must display `"<message>"` to the user. On `"success"`, the client may display `"<message>"` to the user. - -### Form Group Configuration - -As part of the server response, the JSON stream may contain a key -`form-update`. It contains an array of objects having following -structure - - { - ... - "form-update" : [ - { - "form-element": "<element_name>", - "hidden": true | false, - "disabled": true | false, - "required": true | false, - "value": <value> - }, - ... - ], - ... - } - - -`"form-element"` -: the name of the HTML Form Element as it appears in the `name` - attribute. - -`"hidden"` -: whether the Form Group is visible (value: `false`) or - invisible (value: `true`). - -`"disabled"` -: whether or not the Form Element is disabled HTML-wise. - -`"required"` -: whether or not the Form Element receives the HTML5 `required` - attribute. - -`"value"` -: For textual HTML Form Input elements, it is supposed to be a - scalar value, which is set on the element. - - When `"form-element"` references a `<select>` element, a scalar value - selects the corresponding value from the option list. In order to replace - the option list, use an array of objects, having this format - - [ - { - "value": 100, - "text": "a", - "selected": true - }, - { - "value": 200, - "text": "b", - "selected": false - } - ] - - `"select"` is optional, as is `"text"`. If `"text"` is omitted, it - will be derived from value. - - -Form load (update) ------------------- - -### Trigger - -Form Element with attribute `data-load="data-load"`. - -The client side JavaScript installs on change handlers for all HTML Form Elements having the `data-load` attribute. - -### Request: api/load.php - -#### Type -POST - -#### Parameters - -##### URL -none - -##### POST - -HTML Form without `<input>` elements of type `file`. The HTML Form is -required to have a HTML Form Element named `s`, which must contain the -SIP. - -### Response - -JSON Stream - - { - "status": "success"|"error", - "message": "<message>", - "redirect": "client"|"url"|"no", - "field-name": "<field name>", - "field-message": "<message>", - "form-update": [ - { - "form-element": "<element_name>", - "hidden": true | false, - "disabled": true | false, - "required": true | false, - "value": <value> - } - ] - } - -status -: see [General] - -message -: see [General] - -redirect -: not used - -field-name -: HTML Form Element Name which raised error on server side. Requires - status to be `"error"` - -field-message -: reason of error. Requires status to be `"error"`. - -form-update -: Array of Objects. Each object describes the state and value of a - HTML Form Element identfied by its `name` attribute. - - -Form save ---------- - -### Trigger -none - -### Request: api/save.php - -#### Type -POST - -#### Parameters - -##### URL -none - -##### POST -HTML Form without `<input>` elements of type `file`. The HTML Form is required to have a HTML Form Element named `s`, which must contain the SIP. - -### Response - -JSON Stream - - { - "status": "success"|"error", - "message": "<message>", - "redirect": "client"|"url"|"no", - "field-name": "<field name>", - "field-message": "<message>", - "form-update": [ - { - "form-element": "<element_name>", - "hidden": true | false, - "disabled": true | false, - "required": true | false, - "value": <value> - } - ] - } - -Name | Description -------- | ----------- -status | see General -message | see General -redirect | not used -field-name | HTML Form Element Name which raised error on server side. Requires status to be `"error"` -field-message | reason of error. Requires status to be `"error"`. -form-update | Array of Objects. Each object describes the state and value of a HTML Form Element identfied by its `name` attribute. - - -File (upload) -------------- - -### Trigger -none - -### Request: api/file.php - -#### Type -POST - -#### Parameters - -##### URL -`action=upload` - -##### POST -Multi part form with file content, parameter `s` containing SIP, and parameter `name` containing the name of the HTML Form Element. - -### Response - -JSON Stream - - { - "status": "success"|"error", - "message": "<message>" - } - - -Name | Description -------- | ----------- -status | see General -message | see General - - -Record delete -------------- - -Request: api/delete.php - - -Return JSON encoded answer - -status: success|error -message: <message> -redirect: client|url|no -redirect-url: <url> -field-name:<field name> -field-message: <message> - -Description: - -Delete successfull. - status = 'success' - message = <message> - redirect = 'client' - -Delete successfull. - status = 'success' - message = <message> - redirect = 'url' - redirect-url = <URL> - -Delete failed: Show message. - status = 'error' - message = <message> - redirect = 'no' - -## Glossary - -SIP -: tbd - -HTML Form Element -: Any `<input>` or `<select>` HTML tag. Synonymous to *Form Element*. - -Form Group -: The sourrounding `<div>` containing the `.control-label`, - `.form-control` `<div>`s, and `.help-block` `<p>`. diff --git a/Gruntfile.js b/Gruntfile.js index d627afb60b2c96f1159e42d81030d69abc280855..9aeb4e55784729fa823ab1a09097b77d31c51bf7 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -315,8 +315,8 @@ module.exports = function (grunt) { } }, jasmine: { - frontend: { - src: ['tests/jasmine/spec/*Spec.js'], + unit: { + src: ['tests/jasmine/unit/spec/*Spec.js'], options: { vendor: [ 'js/jquery.min.js', @@ -326,7 +326,7 @@ module.exports = function (grunt) { 'js/qfq.debug.js' ], helpers: ['tests/jasmine/helper/mock-ajax.js'], - template: 'tests/jasmine/SpecRunner.tmpl' + template: 'tests/jasmine/unit/SpecRunner.tmpl' } } }, diff --git a/PROTOCOL.md b/PROTOCOL.md new file mode 100644 index 0000000000000000000000000000000000000000..d9f71454bc715abe4a9fba50c5bcfbaf62700026 --- /dev/null +++ b/PROTOCOL.md @@ -0,0 +1,299 @@ +<!-- -*- markdown -*- --> + +# Client/Server Protocol + + +## General Protocol + +The Client may asynchronously send requests to the Server. The Server +is expected to return responses as outlined below. + +The response must contain at least a [Minimal Response]. Depending on +the request, it may provide additional responses as outlined in this +section. + + +### Minimal Response + +Asynchronous request (read AJAX) initiated by the Client receive a +JSON Response from the server containing at least: + + { + "status": "success"|"error", + "message": "<message>" + } + +`status` indicates whether or not the request has been fullfiled by +the server (`"success"`) or encountered an error (`"error"`). On +`"error"` the Client must display `"<message>"` to the user. On +`"success"`, the Client may display `"<message>"` to the user. + +Depending on the request, the server may provide additional +information in the response, as outlined below. + + +### HTML Form Element Validation Response + +The Server may perform a serverside validation of values submitted as +part of a HTML Form submission. If the validation fails, it may notify +the Client by adding following name/value pairs to the response JSON +Stream + + { + "status": "error", + ... + "field-name": "<field name>", + "field-message": "<message>", + ... + } + +Only one validation failure per request can be reported to Client. + +The Server is expected to set the status `"status"` to `"error"`, and +the Client is expected to treat the error as explained in [Minimal Response] +and must obey the rules of redirection as explained in [Redirection Response]. + +The Client must visibly highlight the HTML Form Element that caused the +validation failure. + +`"field-name"` +: The value of the `name` attribute of the HTML Form Element that + caused the validation failure. + +`"field-message"` +: Message to the User, indicating the nature of the failure. + + +### Form Group Configuration Response + +As part of the server response, the JSON stream may contain a key +`form-update`. This response is used to reconfigure HTML Form Elements +and Form Groups on the clientside, based on conditions evaluated on +the serverside. It contains an array of objects +having the following structure + + { + ... + "form-update" : [ + { + "form-element": "<element_name>", + "hidden": true | false, + "disabled": true | false, + "required": true | false, + "value": <value> + }, + ... + ], + ... + } + +`"form-element"` +: the name of the HTML Form Element as it appears in the `name` attribute. + +`"hidden"` +: whether the Form Group is visible (value: `false`) or invisible (value: `true`). + +`"disabled"` +: whether or not the Form Element is disabled HTML-wise. + +`"required"` +: whether or not the Form Element receives the HTML5 `required` attribute. + +`"value"` +: For textual HTML Form Input elements, it is supposed to be a scalar + value, which is set on the element. + + When `"form-element"` references a `<select>` element, a scalar + value selects the corresponding value from the option list. In + order to replace the entire option list, use an array of objects + as value to `"value"`, having this format + + [ + ... + { + "value": 100, + "text": "a", + "selected": true + }, + { + "value": 200, + "text": "b", + "selected": false + } + ... + ] + + `"select"` is optional, as is `"text"`. If `"text"` is omitted, it + will be derived from value. + + HTML checkboxes are ticked with a `"value"` of `true`. They are + unchecked with `false`. + + HTML radio buttons are activated by providing the value of the + radio button `value`-attribute to be activated in `"value"`. + + +### Redirection Response + +Depending on the request, the server may return redirection +information to the Client. It is up to the Client to respect the +redirection information. + +The Client must not perform a redirect in case the status in +`"status"` is `"error"`. + +The format of redirect information is outlined below + + { + ... + "redirect": "no" | "url" | "client" + "redirect-url": "<url>" + ... + } + + +`"redirect"` +: type of redirection. `"no"` advises the Client to stay on the + Current Page. `"client"` advises the Client to decide where to + redirect to. `"url"` advices the Client to redirect to the URL + provided in `"redirect-url"`. + +`"redirect-url"` +: Used to provide an URL when `"redirect"` is set to `"url"`. It + should be disregarded unless `"redirect"` is set to `"url"`. + + +## API Endpoints + + +### Form Update + +The Client may request an updated set of Form Group Configuration and +HTLM Element states. In order for the Server to compile the set of +Form Group Configuration and HTML Element states, it requires the +entire HTML Form in the POST body, without any HTML Input Elements of +type `file`. + +The Client must include the SIP using an HTML Input Element (most +likely of `type` `hidden`). + +Request URL +: api/load.php + +Request Method +: POST + +URL Parameters +: none + +Server Response +: The response contains at least a [Minimal Response]. In addition, + a [Form Group Configuration Response] may be included. + + +### Form Save + +The Client submits the HTML Form for persitent storage to the +Server. The submission should not contain `<input>` HTML Elements of +type `file`. + +The Client must include the SIP using an HTML Input Element (most +likely of `type` `hidden`). + +Request URL +: api/save.php + +Request Method +: POST + +URL Parameters +: none + +Server Response +: The response contains at least a [Minimal Response]. In addition, a + [Form Group Configuration Response], + [HTML Form Element Validation Response] and/or + [Redirection Response] may be included. + + +### File Upload + +Files are uploaded asynchronously. Each file to be uploaded requires +one request to the Server, using a Multi part form with file content, +parameter `s` containing SIP, and parameter `name` containing the name +of the HTML Form Element. + +Request +: api/file.php + +Request Method +: POST + +URL Parameters +: `action=upload` + +Server Response +: The response contains a [Minimal Response]. + + +### File Delete + +Files are delete asynchronously. Each file to be delete on the +serverside requires on request to the Server. The parameters +identifying the file to be deleted are sent as part of the POST +body. The SIP of the request is included in the parameter name +`s`. The value of the `name` attribute of the HTML Form Element is +provided in `name`. + +Request +: api/file.php + +Request Method +: POST + +URL Parameters +: `action=delete` + +Server Response +: The response contains a [Minimal Response]. + + +### Record delete + +Request the deletion of the record identified by the SIP. + +Request +: api/delete.php + +Request Method +: POST + +URL Parameters +: `s=<SIP>` + +Server Response +: The response contains a [Minimal Response]. + + +## Glossary + +SIP +: tbd + +HTML Form Element +: Any `<input>` or `<select>` HTML tag. Synonymous to *Form Element*. + +Form Group +: The sourrounding `<div>` containing the `.control-label`, + `.form-control` `<div>`s, and `.help-block` `<p>`. + +Client +: Application that enables a user to interact with QFQ, i.e. a Web Browser. + + +Current Page +: The currently displayed page in the Client. + +Redirect +: Issued by the Server. It is a command prompting the Client to + navigate away from the Current Page. diff --git a/javascript/src/Helper/FunctionList.js b/javascript/src/Helper/FunctionList.js deleted file mode 100644 index f0ccd20cb3907766636b03d0f5fee5123f8cb188..0000000000000000000000000000000000000000 --- a/javascript/src/Helper/FunctionList.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * @author Rafael Ostertag <rafael.ostertag@math.uzh.ch> - */ - -/* global $ */ - -var QfqNS = QfqNS || {}; -QfqNS.Helper = QfqNS.Helper || {}; - -(function (n) { - 'use strict'; - - /** - * @deprecated - * @constructor - */ - n.FunctionList = function () { - this.functions = []; - }; - - n.FunctionList.prototype.addFunction = function (func, thisObj) { - if (typeof func !== "function") { - throw new TypeError("Expected function"); - } - - if (thisObj) { - this.functions.push([func, thisObj]); - } else { - this.functions.push(func); - } - }; - - n.FunctionList.prototype.call = function () { - var _arguments = arguments; - this.functions.forEach(function (func) { - if (typeof func === "function") { - func.apply(undefined, _arguments); - } else { - // func is an array [ Function, thisObj ] - func[0].apply(func[1], _arguments); - } - }); - }; -})(QfqNS.Helper); \ No newline at end of file diff --git a/tests/jasmine/acceptance/spec/FileUploadSpec.js b/tests/jasmine/acceptance/spec/FileUploadSpec.js new file mode 100644 index 0000000000000000000000000000000000000000..6423d519e9990c88064e80fd5d957d154b5032ac --- /dev/null +++ b/tests/jasmine/acceptance/spec/FileUploadSpec.js @@ -0,0 +1,45 @@ +/** + * @author Rafael Ostertag <rafael.ostertag@math.uzh.ch> + */ + +/* global describe */ +/* global it */ +/* global expect */ +/* global QfqNS */ +/* global beforeAll */ +/* global beforeEach */ +/* global jasmine */ +/* global $ */ + +describe("File Upload Acceptance", function () { + 'use strict'; + + function prepareForm(id) { + var $form; + var $template = $("#" + id); + + $form = $('#myForm').empty(); + $form.append($template.text()); + + } + + it("handles no previous uploaded file present", function () { + prepareForm("fileupload_new"); + + jasmine.Ajax.install(); + + var bla = $('#fileupload')[0]; + var qfqForm = new QfqNS.QfqForm("myForm", "", "", "", "", ""); + + $('#fileupload').trigger("change"); + + jasmine.Ajax.requests.mostRecent().respondWith({ + "status": 200, + "contentType": "application/json", + "responseText": "{ \"status\": \"success\" }" + }); + + jasmine.Ajax.uninstall(); + }); + +}); \ No newline at end of file diff --git a/tests/jasmine/spec/HelperFunctionListSpec.js b/tests/jasmine/spec/HelperFunctionListSpec.js deleted file mode 100644 index b7c0a76d9872ec5be79e86ce28a09e860b59948c..0000000000000000000000000000000000000000 --- a/tests/jasmine/spec/HelperFunctionListSpec.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * @author Rafael Ostertag <rafael.ostertag@math.uzh.ch> - */ - -/* global describe */ -/* global it */ -/* global expect */ -/* global QfqNS */ -/* global beforeAll */ -/* global beforeEach */ -/* global jasmine */ - -describe("Helper FunctionList", function () { - 'use strict'; - - var functionList; - - beforeEach(function () { - functionList = new QfqNS.Helper.FunctionList(); - }); - - it("should call one function properly", function () { - var spy = jasmine.createSpy('SingleFunction'); - - functionList.addFunction(spy); - functionList.call("1", 2, {}); - - expect(spy).toHaveBeenCalled(); - expect(spy).toHaveBeenCalledWith("1", 2, {}); - - }); - - it("should call several functions properly", function () { - var spy1 = jasmine.createSpy('Function1'); - var spy2 = jasmine.createSpy('Function2'); - var spy3 = jasmine.createSpy('Function3'); - - functionList.addFunction(spy1); - functionList.addFunction(spy2); - functionList.addFunction(spy3); - - functionList.call("1", 2, {}); - - expect(spy1).toHaveBeenCalled(); - expect(spy1).toHaveBeenCalledWith("1", 2, {}); - - expect(spy2).toHaveBeenCalled(); - expect(spy2).toHaveBeenCalledWith("1", 2, {}); - - expect(spy3).toHaveBeenCalled(); - expect(spy3).toHaveBeenCalledWith("1", 2, {}); - - }); - - it("should pass proper object as `this'", function () { - var obj = { - val: 0, - f: function (a) { - this.val = a; - } - }; - - var spy1 = jasmine.createSpy('Function1'); - - functionList.addFunction(obj.f, obj); - functionList.addFunction(spy1); - - functionList.call(2); - - expect(spy1).toHaveBeenCalledWith(2); - expect(obj.val).toBe(2); - - }); - - it("should not allow adding anything besides FunctionS", function () { - expect(function () { - functionList.add('bla'); - }).toThrowError(TypeError); - }); - } -); \ No newline at end of file diff --git a/tests/jasmine/MIT.LICENSE b/tests/jasmine/unit/MIT.LICENSE similarity index 100% rename from tests/jasmine/MIT.LICENSE rename to tests/jasmine/unit/MIT.LICENSE diff --git a/tests/jasmine/SpecRunner.html b/tests/jasmine/unit/SpecRunner.html similarity index 92% rename from tests/jasmine/SpecRunner.html rename to tests/jasmine/unit/SpecRunner.html index 8a6e9f4dbdad51c0ebbfd44ac255eaceed883ea7..c1f40d8ccd47447230637da9f241529729cbc811 100644 --- a/tests/jasmine/SpecRunner.html +++ b/tests/jasmine/unit/SpecRunner.html @@ -4,25 +4,24 @@ <meta charset="utf-8"> <title>Jasmine Spec Runner v2.4.1</title> - <link rel="shortcut icon" type="image/png" href="lib/jasmine-2.4.1/jasmine_favicon.png"> - <link rel="stylesheet" href="lib/jasmine-2.4.1/jasmine.css"> - <link rel="stylesheet" href="../../css/bootstrap.min.css"> - <link rel="stylesheet" href="../../css/bootstrap-theme.min.css"> + <link rel="shortcut icon" type="image/png" href="../lib/jasmine-2.4.1/jasmine_favicon.png"> + <link rel="stylesheet" href="../lib/jasmine-2.4.1/jasmine.css"> + <link rel="stylesheet" href="../../../css/bootstrap.min.css"> + <link rel="stylesheet" href="../../../css/bootstrap-theme.min.css"> - <script src="lib/jasmine-2.4.1/jasmine.js"></script> - <script src="lib/jasmine-2.4.1/jasmine-html.js"></script> - <script src="lib/jasmine-2.4.1/boot.js"></script> - <script src="helper/mock-ajax.js"></script> + <script src="../lib/jasmine-2.4.1/jasmine.js"></script> + <script src="../lib/jasmine-2.4.1/jasmine-html.js"></script> + <script src="../lib/jasmine-2.4.1/boot.js"></script> + <script src="../helper/mock-ajax.js"></script> - <script src="../../js/jquery.min.js"></script> - <script src="../../js/bootstrap.min.js"></script> - <script src="../../js/EventEmitter.min.js"></script> + <script src="../../../js/jquery.min.js"></script> + <script src="../../../js/bootstrap.min.js"></script> + <script src="../../../js/EventEmitter.min.js"></script> <!-- include source files here... --> - <script src="../../js/qfq.debug.js"></script> + <script src="../../../js/qfq.debug.js"></script> <!-- include spec files here... --> - <script src="spec/HelperFunctionListSpec.js"></script> <script src="spec/ElementFormGroupSpec.js"></script> <script src="spec/ElementTextualSpec.js"></script> <script src="spec/ElementRadioSpec.js"></script> diff --git a/tests/jasmine/SpecRunner.tmpl b/tests/jasmine/unit/SpecRunner.tmpl similarity index 100% rename from tests/jasmine/SpecRunner.tmpl rename to tests/jasmine/unit/SpecRunner.tmpl diff --git a/tests/jasmine/spec/BSTabsSpec.js b/tests/jasmine/unit/spec/BSTabsSpec.js similarity index 100% rename from tests/jasmine/spec/BSTabsSpec.js rename to tests/jasmine/unit/spec/BSTabsSpec.js diff --git a/tests/jasmine/spec/ElementCheckboxSpec.js b/tests/jasmine/unit/spec/ElementCheckboxSpec.js similarity index 100% rename from tests/jasmine/spec/ElementCheckboxSpec.js rename to tests/jasmine/unit/spec/ElementCheckboxSpec.js diff --git a/tests/jasmine/spec/ElementFormGroupSpec.js b/tests/jasmine/unit/spec/ElementFormGroupSpec.js similarity index 100% rename from tests/jasmine/spec/ElementFormGroupSpec.js rename to tests/jasmine/unit/spec/ElementFormGroupSpec.js diff --git a/tests/jasmine/spec/ElementRadioSpec.js b/tests/jasmine/unit/spec/ElementRadioSpec.js similarity index 100% rename from tests/jasmine/spec/ElementRadioSpec.js rename to tests/jasmine/unit/spec/ElementRadioSpec.js diff --git a/tests/jasmine/spec/ElementSelectSpec.js b/tests/jasmine/unit/spec/ElementSelectSpec.js similarity index 100% rename from tests/jasmine/spec/ElementSelectSpec.js rename to tests/jasmine/unit/spec/ElementSelectSpec.js diff --git a/tests/jasmine/spec/ElementSpec.js b/tests/jasmine/unit/spec/ElementSpec.js similarity index 100% rename from tests/jasmine/spec/ElementSpec.js rename to tests/jasmine/unit/spec/ElementSpec.js diff --git a/tests/jasmine/spec/ElementTextualSpec.js b/tests/jasmine/unit/spec/ElementTextualSpec.js similarity index 100% rename from tests/jasmine/spec/ElementTextualSpec.js rename to tests/jasmine/unit/spec/ElementTextualSpec.js diff --git a/tests/jasmine/spec/FormSpec.js b/tests/jasmine/unit/spec/FormSpec.js similarity index 100% rename from tests/jasmine/spec/FormSpec.js rename to tests/jasmine/unit/spec/FormSpec.js diff --git a/tests/jasmine/spec/PageTitleSpec.js b/tests/jasmine/unit/spec/PageTitleSpec.js similarity index 100% rename from tests/jasmine/spec/PageTitleSpec.js rename to tests/jasmine/unit/spec/PageTitleSpec.js