diff --git a/cypress/integration/persons.js b/cypress/integration/persons.js index d32016b..a296afc 100644 --- a/cypress/integration/persons.js +++ b/cypress/integration/persons.js @@ -1,264 +1,424 @@ /** * Copyright (C) 2020 The Software Heritage developers * See the AUTHORS file at the top-level directory of this distribution * License: GNU Affero General Public License version 3, or any later version * See top-level LICENSE file for more information */ /* * Tests the author/contributor dynamic fieldsets */ "use strict"; describe('Zero author', function() { it('can be exported', function() { cy.get('#name').type('My Test Software'); cy.get('#generateCodemeta').click(); cy.get('#errorMessage').should('have.text', ''); cy.get('#codemetaText').then((elem) => JSON.parse(elem.text())) .should('deep.equal', { "@context": "https://doi.org/10.5063/schema/codemeta-2.0", "@type": "SoftwareSourceCode", "name": "My Test Software", }); }); it('can be imported from no list', function() { cy.get('#codemetaText').then((elem) => elem.text(JSON.stringify({ "@context": "https://doi.org/10.5063/schema/codemeta-2.0", "@type": "SoftwareSourceCode", "name": "My Test Software", })) ); cy.get('#importCodemeta').click(); cy.get('#author_nb').should('have.value', '0'); cy.get('#author_0').should('not.exist'); cy.get('#author_1').should('not.exist'); }); it('can be imported from empty list', function() { cy.get('#codemetaText').then((elem) => elem.text(JSON.stringify({ "@context": "https://doi.org/10.5063/schema/codemeta-2.0", "@type": "SoftwareSourceCode", "name": "My Test Software", "author": [], })) ); cy.get('#importCodemeta').click(); cy.get('#author_nb').should('have.value', '0'); cy.get('#author_0').should('not.exist'); cy.get('#author_1').should('not.exist'); }); }); describe('One full author', function() { it('can be exported', function() { cy.get('#name').type('My Test Software'); cy.get('#author_nb').should('have.value', '0'); cy.get('#author_0').should('not.exist'); cy.get('#author_1').should('not.exist'); cy.get('#author_1_givenName').should('not.exist'); cy.get('#author_add').click(); cy.get('#author_nb').should('have.value', '1'); cy.get('#author_0').should('not.exist'); cy.get('#author_1').should('exist'); cy.get('#author_2').should('not.exist'); cy.get('#author_1_givenName').should('have.value', ''); cy.get('#author_1_familyName').should('have.value', ''); cy.get('#author_1_email').should('have.value', ''); cy.get('#author_1_id').should('have.value', ''); cy.get('#author_1_affiliation').should('have.value', ''); cy.get('#author_1_givenName').type('Jane'); cy.get('#author_1_familyName').type('Doe'); cy.get('#author_1_email').type('jdoe@example.org'); cy.get('#author_1_id').type('http://example.org/~jdoe'); cy.get('#author_1_affiliation').type('http://example.org/'); cy.get('#generateCodemeta').click(); cy.get('#errorMessage').should('have.text', ''); cy.get('#codemetaText').then((elem) => JSON.parse(elem.text())) .should('deep.equal', { "@context": "https://doi.org/10.5063/schema/codemeta-2.0", "@type": "SoftwareSourceCode", "name": "My Test Software", "author": [ { "@type": "Person", "@id": "http://example.org/~jdoe", "givenName": "Jane", "familyName": "Doe", "email": "jdoe@example.org", "affiliation": { "@type": "Organization", "@id": "http://example.org/", } } ], }); }); it('can be imported', function() { cy.get('#codemetaText').then((elem) => elem.text(JSON.stringify({ "@context": "https://doi.org/10.5063/schema/codemeta-2.0", "@type": "SoftwareSourceCode", "name": "My Test Software", "author": [ { "@type": "Person", "@id": "http://example.org/~jdoe", "givenName": "Jane", "familyName": "Doe", "email": "jdoe@example.org", "affiliation": { "@type": "Organization", "@id": "http://example.org/", } } ], })) ); cy.get('#importCodemeta').click(); cy.get('#author_nb').should('have.value', '1'); cy.get('#author_0').should('not.exist'); cy.get('#author_1').should('exist'); cy.get('#author_2').should('not.exist'); cy.get('#author_1_givenName').should('have.value', 'Jane'); cy.get('#author_1_familyName').should('have.value', 'Doe'); cy.get('#author_1_email').should('have.value', 'jdoe@example.org'); cy.get('#author_1_id').should('have.value', 'http://example.org/~jdoe'); cy.get('#author_1_affiliation').should('have.value', 'http://example.org/'); }); }); describe('Affiliation id', function() { it('can be exported', function() { cy.get('#name').type('My Test Software'); cy.get('#author_add').click(); cy.get('#author_1_givenName').type('Jane'); cy.get('#author_1_affiliation').type('http://example.org/'); cy.get('#generateCodemeta').click(); cy.get('#errorMessage').should('have.text', ''); cy.get('#codemetaText').then((elem) => JSON.parse(elem.text())) .should('deep.equal', { "@context": "https://doi.org/10.5063/schema/codemeta-2.0", "@type": "SoftwareSourceCode", "name": "My Test Software", "author": [ { "@type": "Person", "givenName": "Jane", "affiliation": { "@type": "Organization", "@id": "http://example.org/", } } ], }); }); it('can be imported', function() { cy.get('#codemetaText').then((elem) => elem.text(JSON.stringify({ "@context": "https://doi.org/10.5063/schema/codemeta-2.0", "@type": "SoftwareSourceCode", "name": "My Test Software", "author": [ { "@type": "Person", "@id": "http://example.org/~jdoe", "givenName": "Jane", "familyName": "Doe", "email": "jdoe@example.org", "affiliation": { "@type": "Organization", "@id": "http://example.org/", } } ], })) ); cy.get('#importCodemeta').click(); cy.get('#author_nb').should('have.value', '1'); cy.get('#author_0').should('not.exist'); cy.get('#author_1').should('exist'); cy.get('#author_2').should('not.exist'); cy.get('#author_1_affiliation').should('have.value', 'http://example.org/'); }); }); describe('Affiliation name', function() { it('can be exported', function() { cy.get('#name').type('My Test Software'); cy.get('#author_add').click(); cy.get('#author_1_givenName').type('Jane'); cy.get('#author_1_affiliation').type('Example Org'); cy.get('#generateCodemeta').click(); cy.get('#errorMessage').should('have.text', ''); cy.get('#codemetaText').then((elem) => JSON.parse(elem.text())) .should('deep.equal', { "@context": "https://doi.org/10.5063/schema/codemeta-2.0", "@type": "SoftwareSourceCode", "name": "My Test Software", "author": [ { "@type": "Person", "givenName": "Jane", "affiliation": { "@type": "Organization", "name": "Example Org", } } ], }); }); it('can be imported', function() { cy.get('#codemetaText').then((elem) => elem.text(JSON.stringify({ "@context": "https://doi.org/10.5063/schema/codemeta-2.0", "@type": "SoftwareSourceCode", "name": "My Test Software", "author": [ { "@type": "Person", "@id": "http://example.org/~jdoe", "givenName": "Jane", "familyName": "Doe", "email": "jdoe@example.org", "affiliation": { "@type": "Organization", "name": "Example Org", } } ], })) ); cy.get('#importCodemeta').click(); cy.get('#author_nb').should('have.value', '1'); cy.get('#author_0').should('not.exist'); cy.get('#author_1').should('exist'); cy.get('#author_2').should('not.exist'); cy.get('#author_1_affiliation').should('have.value', 'Example Org'); }); }); + +describe('Author order change', function() { + it('is a noop with a single author', function() { + cy.get('#name').type('My Test Software'); + + cy.get('#author_add').click(); + cy.get('#author_1_givenName').type('Jane'); + cy.get('#author_1_affiliation').type('Example Org'); + + cy.get('#author_1_moveToRight').click() + + cy.get('#author_1_givenName').should('have.value', 'Jane'); + cy.get('#author_1_affiliation').should('have.value', 'Example Org'); + + cy.get('#author_1_moveToLeft').click() + + cy.get('#author_1_givenName').should('have.value', 'Jane'); + cy.get('#author_1_affiliation').should('have.value', 'Example Org'); + }); + + it('flips two authors', function() { + cy.get('#name').type('My Test Software'); + + cy.get('#author_add').click(); + cy.get('#author_add').click(); + cy.get('#author_add').click(); + cy.get('#author_1_givenName').type('Jane'); + cy.get('#author_1_affiliation').type('Example Org'); + cy.get('#author_2_givenName').type('John'); + cy.get('#author_2_familyName').type('Doe'); + cy.get('#author_3_givenName').type('Alex'); + + cy.get('#author_1_moveToRight').click() + + cy.get('#author_1_givenName').should('have.value', 'John'); + cy.get('#author_1_familyName').should('have.value', 'Doe'); + cy.get('#author_1_affiliation').should('have.value', ''); + + cy.get('#author_2_givenName').should('have.value', 'Jane'); + cy.get('#author_2_familyName').should('have.value', ''); + cy.get('#author_2_affiliation').should('have.value', 'Example Org'); + + cy.get('#author_3_givenName').should('have.value', 'Alex'); + cy.get('#author_3_familyName').should('have.value', ''); + cy.get('#author_3_affiliation').should('have.value', ''); + }); + + it('updates generated Codemeta', function() { + cy.get('#name').type('My Test Software'); + + cy.get('#author_add').click(); + cy.get('#author_add').click(); + cy.get('#author_1_givenName').type('Jane'); + cy.get('#author_1_affiliation').type('Example Org'); + cy.get('#author_2_givenName').type('John'); + cy.get('#author_2_familyName').type('Doe'); + + cy.get('#generateCodemeta').click(); + + cy.get('#codemetaText').then((elem) => JSON.parse(elem.text())) + .should('deep.equal', { + "@context": "https://doi.org/10.5063/schema/codemeta-2.0", + "@type": "SoftwareSourceCode", + "name": "My Test Software", + "author": [ + { + "@type": "Person", + "givenName": "Jane", + "affiliation": { + "@type": "Organization", + "name": "Example Org", + } + }, + { + "@type": "Person", + "givenName": "John", + "familyName": "Doe", + }, + ], + }); + + cy.get('#author_1_moveToRight').click(); + + cy.get('#codemetaText').then((elem) => JSON.parse(elem.text())) + .should('deep.equal', { + "@context": "https://doi.org/10.5063/schema/codemeta-2.0", + "@type": "SoftwareSourceCode", + "name": "My Test Software", + "author": [ + { + "@type": "Person", + "givenName": "John", + "familyName": "Doe", + }, + { + "@type": "Person", + "givenName": "Jane", + "affiliation": { + "@type": "Organization", + "name": "Example Org", + } + }, + ], + }); + }); + + it('wraps around to the right', function() { + cy.get('#name').type('My Test Software'); + + cy.get('#author_add').click(); + cy.get('#author_add').click(); + cy.get('#author_add').click(); + cy.get('#author_1_givenName').type('Jane'); + cy.get('#author_1_affiliation').type('Example Org'); + cy.get('#author_2_givenName').type('John'); + cy.get('#author_2_familyName').type('Doe'); + cy.get('#author_3_givenName').type('Alex'); + + cy.get('#author_1_moveToLeft').click() + + cy.get('#author_1_givenName').should('have.value', 'Alex'); + cy.get('#author_1_familyName').should('have.value', ''); + cy.get('#author_1_affiliation').should('have.value', ''); + + cy.get('#author_2_givenName').should('have.value', 'John'); + cy.get('#author_2_familyName').should('have.value', 'Doe'); + cy.get('#author_2_affiliation').should('have.value', ''); + + cy.get('#author_3_givenName').should('have.value', 'Jane'); + cy.get('#author_3_familyName').should('have.value', ''); + cy.get('#author_3_affiliation').should('have.value', 'Example Org'); + }); + + it('wraps around to the left', function() { + cy.get('#name').type('My Test Software'); + + cy.get('#author_add').click(); + cy.get('#author_add').click(); + cy.get('#author_add').click(); + cy.get('#author_1_givenName').type('Jane'); + cy.get('#author_1_affiliation').type('Example Org'); + cy.get('#author_2_givenName').type('John'); + cy.get('#author_2_familyName').type('Doe'); + cy.get('#author_3_givenName').type('Alex'); + + cy.get('#author_3_moveToRight').click() + + cy.get('#author_1_givenName').should('have.value', 'Alex'); + cy.get('#author_1_familyName').should('have.value', ''); + cy.get('#author_1_affiliation').should('have.value', ''); + + cy.get('#author_2_givenName').should('have.value', 'John'); + cy.get('#author_2_familyName').should('have.value', 'Doe'); + cy.get('#author_2_affiliation').should('have.value', ''); + + cy.get('#author_3_givenName').should('have.value', 'Jane'); + cy.get('#author_3_familyName').should('have.value', ''); + cy.get('#author_3_affiliation').should('have.value', 'Example Org'); + }); +}) diff --git a/index.html b/index.html index 691f4ec..38f7cbe 100644 --- a/index.html +++ b/index.html @@ -1,357 +1,355 @@ CodeMeta generator

CodeMeta generator

Most fields are optional. Mandatory fields will be highlighted when generating Codemeta.

The software itself

the software title


from SPDX licence list

Discoverability and citation


such as ISBNs, GTIN codes, UUIDs etc.. http://schema.org/identifier


grant funding software development


organization funding software development

Authors and contributors can be added below
Development community / tools


Run-time environment


Current version of the software


Additional Info


see www.repostatus.org for details

Authors -

Order of authors does not matter.

-
Contributors

Order of contributors does not matter.

codemeta.json:


   
diff --git a/js/dynamic_form.js b/js/dynamic_form.js index 8fef145..15b2785 100644 --- a/js/dynamic_form.js +++ b/js/dynamic_form.js @@ -1,131 +1,186 @@ /** * Copyright (C) 2019 The Software Heritage developers * See the AUTHORS file at the top-level directory of this distribution * License: GNU Affero General Public License version 3, or any later version * See top-level LICENSE file for more information */ "use strict"; +// List of all HTML fields in a Person fieldset. +const personFields = [ + 'givenName', + 'familyName', + 'email', + 'id', + 'affiliation', +]; + function createPersonFieldset(personPrefix, legend) { // Creates a fieldset containing inputs for informations about a person var fieldset = document.createElement("fieldset") + var moveButtons; fieldset.classList.add("person"); fieldset.classList.add("leafFieldset"); fieldset.id = personPrefix; fieldset.innerHTML = ` ${legend} +
+ + +

`; return fieldset; } function addPersonWithId(container, prefix, legend, id) { - var fieldset = createPersonFieldset(`${prefix}_${id}`, `${legend} #${id}`); + var personPrefix = `${prefix}_${id}`; + var fieldset = createPersonFieldset(personPrefix, `${legend} #${id}`); container.appendChild(fieldset); + + document.querySelector(`#${personPrefix}_moveToLeft`) + .addEventListener('click', () => movePerson(prefix, id, "left")); + document.querySelector(`#${personPrefix}_moveToRight`) + .addEventListener('click', () => movePerson(prefix, id, "right")); +} + +function movePerson(prefix, id1, direction) { + var nbPersons = getNbPersons(prefix); + var id2; + + // Computer id2, the id of the person to flip id1 with (wraps around the + // end of the list of persons) + if (direction == "left") { + id2 = id1 - 1; + if (id2 <= 0) { + id2 = nbPersons; + } + } + else { + id2 = id1 + 1; + if (id2 > nbPersons) { + id2 = 1; + } + } + + // Flip the field values, one by one + personFields.forEach((fieldName) => { + var field1 = document.querySelector(`#${prefix}_${id1}_${fieldName}`); + var field2 = document.querySelector(`#${prefix}_${id2}_${fieldName}`); + var value1 = field1.value; + var value2 = field2.value; + field2.value = value1; + field1.value = value2; + }); + + // Form was changed; regenerate + generateCodemeta(); } function addPerson(prefix, legend) { var container = document.querySelector(`#${prefix}_container`); var personId = getNbPersons(prefix) + 1; addPersonWithId(container, prefix, legend, personId); setNbPersons(prefix, personId); return personId; } function removePerson(prefix) { var personId = getNbPersons(prefix); document.querySelector(`#${prefix}_${personId}`).remove(); setNbPersons(prefix, personId-1); } // Initialize a group of persons (authors, contributors) on page load. // Useful if the page is reloaded. function initPersons(prefix, legend) { var nbPersons = getNbPersons(prefix); var personContainer = document.querySelector(`#${prefix}_container`) for (let personId = 1; personId <= nbPersons; personId++) { addPersonWithId(personContainer, prefix, legend, personId); } } function removePersons(prefix) { var nbPersons = getNbPersons(prefix); var personContainer = document.querySelector(`#${prefix}_container`) for (let personId = 1; personId <= nbPersons; personId++) { removePerson(prefix) } } function resetForm() { removePersons('author'); removePersons('contributor'); // Reset the form after deleting elements, so nbPersons doesn't get // reset before it's read. document.querySelector('#inputForm').reset(); } function fieldToLower(event) { event.target.value = event.target.value.toLowerCase(); } function initCallbacks() { document.querySelector('#license') .addEventListener('change', validateLicense); document.querySelector('#generateCodemeta') .addEventListener('click', generateCodemeta); document.querySelector('#resetForm') .addEventListener('click', resetForm); document.querySelector('#validateCodemeta') .addEventListener('click', () => parseAndValidateCodemeta(true)); document.querySelector('#importCodemeta') .addEventListener('click', importCodemeta); document.querySelector('#inputForm') .addEventListener('change', generateCodemeta); document.querySelector('#developmentStatus') .addEventListener('change', fieldToLower); initPersons('author', 'Author'); initPersons('contributor', 'Contributor'); } diff --git a/main.css b/main.css index 3cdd50f..dfa7c62 100644 --- a/main.css +++ b/main.css @@ -1,72 +1,78 @@ /** * Copyright (C) 2019 The Software Heritage developers * See the AUTHORS file at the top-level directory of this distribution * License: GNU Affero General Public License version 3, or any later version * See top-level LICENSE file for more information */ /* This file contains the main CSS to make the form/application usable, * without being especially pretty. */ #noscriptError { color: red; } .person { display: inline-block; } #inputForm { max-width: 100%; display: flex; flex-wrap: wrap; } /* A fieldset that contains only label/input pairs */ .leafFieldset { flex: auto; } p input, p textarea { width: 100%; box-sizing: border-box; } .dynamicFields { width: 100%; } +.dynamicFields .moveButtons { + width: 100%; + display: flex; + justify-content: space-between; +} + #license { /* License names are long */ min-width: 20em; } #funding { /* Funding names are long */ min-width: 20em; } input[type=URL] { /* URLs are longer than the other fields */ min-width: 20em; } .field-description { color : rgb(100, 104, 103); font-size: small; } #codemetaText { width: 100%; min-height: 10em; border: 1px solid black; } #errorMessage { color: red; } input:invalid { color: red; }