Merge branch 'develop' of https://github.com/tainacan/tainacan into develop

This commit is contained in:
mateuswetah 2018-03-09 11:28:39 -03:00
commit 2d01aa27f6
16 changed files with 294 additions and 153 deletions

View File

@ -1,82 +0,0 @@
context('HTML form submission', function(){
beforeEach(function(){
cy.visit('/login')
})
it('displays errors on login', function(){
cy.get('input[name=log]').type('admin')
cy.get('input[name=pwd]').type('senhaerrada{enter}')
// and still be on the same URL
cy.url().should('include', '/wp-login.php')
})
it('redirects to /dashboard on success', function(){
cy.get('input[name=log]').type('admin')
cy.get('input[name=pwd]').type('admin{enter}')
// we should be redirected to /wp-admin
cy.url().should('include', '/wp-admin')
cy.get('h1').should('contain', 'Dashboard')
})
})
context('HTML form submission with cy.request', function(){
it('can bypass the UI and yet still test log in', function(){
// oftentimes once we have a proper e2e test around logging in
// there is NO more reason to actually use our UI to log in users
// doing so wastes is slow because our entire page has to load,
// all associated resources have to load, we have to fill in the
// form, wait for the form submission and redirection process
//
// with cy.request we can bypass this because it automatically gets
// and sets cookies under the hood. This acts exactly as if the requests
// came from the browser
cy.request({
method: 'POST',
url: '/login', // baseUrl will be prepended to this url
form: true, // indicates the body should be form urlencoded and sets Content-Type: application/x-www-form-urlencoded headers
body: {
log: 'admin',
pwd: 'admin'
}
})
// just to prove we have a session
cy.getCookie('cypress-session-cookie').should('null')
})
})
context('Reusable "login" custom command', function(){
// typically we'd put this in cypress/support/commands.js
// but because this custom command is specific to this example
// we'll keep it here
Cypress.Commands.add('loginByForm', (username, password) => {
Cypress.log({
name: 'loginByForm',
message: username + ' | ' + password
})
return cy.request({
method: 'POST',
url: '/login',
form: true,
body: {
log: username,
pwd: password
}
})
// we should be redirected to /wp-admin
cy.url().should('include', '/wp-admin')
cy.get('h1').should('contain', 'Dashboard')
})
it('test loginByForm', function() {
cy.url().should('include', '/wp-admin')
cy.get('h1').should('contain', 'Dashboard')
// login before each test
cy.loginByForm('admin', 'admin')
})
})

View File

@ -0,0 +1,46 @@
describe('Tainacan Plugin Test', function () {
beforeEach(() => {
cy.loginByUI()
})
context('Collections', function(){
it('canceled collection', function(){
cy.visit('/wp-admin/admin.php?page=tainacan_admin#/collections')
cy.get('h1').should('contain', 'Collections Page')
cy.contains('New Collection').click()
cy.get('[id=tainacan-text-name]').type('Book cancelado')
cy.get('[id=button-cancel-collection-creation]').click()
cy.url().should('contain', '/wp-admin/admin.php?page=tainacan_admin#/collections/')
cy.get('h1').should('contain', 'Collections Page')
cy.get('td').should('not.contain', 'Book cancelado')
})
it('status field blank collection', function(){
cy.visit('/wp-admin/admin.php?page=tainacan_admin#/collections')
cy.get('h1').should('contain', 'Collections Page')
cy.contains('New Collection').click()
cy.get('[id=tainacan-text-name]').type('Ebook status em branco')
cy.get('[id=tainacan-text-description]').type('Importante a organização dos livros para estimular a leitura pelos usuários da biblioteca')
cy.get('[id=button-submit-collection-creation]').click()
cy.get('td').should('contain', 'New Item')
cy.visit('/wp-admin/admin.php?page=tainacan_admin#/collections')
cy.get('h1').should('contain', 'Collections Page')
cy.get('td').should('not.contain', 'Book cancelado')
})
it('create collection', function(){
cy.visit('/wp-admin/admin.php?page=tainacan_admin#/collections')
cy.get('h1').should('contain', 'Collections Page')
cy.contains('New Collection').click()
cy.get('[id=tainacan-text-name]').type('Ebook 1')
cy.get('[id=tainacan-text-description]').type('Importante a organização dos livros para estimular a leitura pelos usuários da biblioteca')
cy.get('[id=tainacan-select-status]').select('Publish').should('have.value', 'publish')
cy.get('[id=button-submit-collection-creation]').click()
cy.get('td').should('contain', 'New Item')
cy.visit('/wp-admin/admin.php?page=tainacan_admin#/collections')
cy.get('td').should('contain', 'Ebook 1')
})
})
})

View File

@ -0,0 +1,18 @@
describe('Configuration Plugin Test', function () {
beforeEach(() => {
cy.loginByUI()
})
context ('Configuration', function(){
it('plugins page', function(){
cy.visit('wp-admin/plugins.php')
cy.get('h1').should('contain', 'Plugins')
})
it('plugin active', function () {
cy.contains('Tainacan').click()
cy.get('h1').should('contain', 'Collections Page')
})
})
})

View File

@ -1,13 +0,0 @@
describe('Plugin active Test', function () {
beforeEach(() => {
cy.loginByForm('admin', 'admin')
})
it('plugin active', function () {
cy.get('h1').should('contain', 'Dashboard')
cy.get('div').should('contain', 'Tainacan')
cy.contains('Tainacan').click()
cy.title().contains('Collections Page')
cy.get('h1').should('contain', 'Collections Page')
})
})

View File

@ -23,24 +23,60 @@
// //
// -- This is will overwrite an existing command -- // -- This is will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
Cypress.Commands.add('loginByForm', (username, password) => { Cypress.Commands.add('loginByForm', (username, password) => {
Cypress.log({ Cypress.log({
name: 'loginByForm', name: 'loginByRequest',
message: username + ' | ' + password message: username + ' | ' + password
}) })
cy.request('/wp-admin')
cy.get('title').should('contain', 'Log In Test — WordPress')
cy.request({
method: 'POST',
url: '/wp-login.php',
form: true,
body: {
log: username,
pwd: password
}
})
cy.get('h1').should('contain', 'Dashboard')
cy.getCookie('cypress-session-cookie').should('exist')
})
Cypress.Commands.add('loginByRequest', () => {
Cypress.log({
name: 'loginByRequest',
message: 'admin' + ' | ' + 'admin'
})
cy.request({ cy.request({
method: 'POST', method: 'POST',
url: '/login', url: '/login',
form: true, form: true,
body: { body: {
log: username, log: 'admin',
pwd: password pwd: 'admin'
} }
}) })
// we should be redirected to /wp-admin // we should be redirected to /wp-admin
cy.url().should('include', '/wp-admin') cy.url().should('include', '/wp-admin')
cy.get('h1').should('contain', 'Dashboard') cy.get('h1').should('contain', 'Dashboard')
cy.getCookie('cypress-session-cookie').should('exist')
}) })
Cypress.Commands.add('loginByUI', () => {
cy.visit('/wp-admin')
cy.get('input[name=log]').type('admin')
cy.get('input[name=pwd]').type('admin{enter}')
// we should be redirected to /wp-admin
cy.url().should('include', '/wp-admin')
cy.get('h1').should('contain', 'Dashboard')
})

View File

@ -14,6 +14,7 @@ import Relationship from '../../classes/field-types/relationship/Relationship.vu
import FormRelationship from '../../classes/field-types/relationship/FormRelationship.vue'; import FormRelationship from '../../classes/field-types/relationship/FormRelationship.vue';
import FormCategory from '../../classes/field-types/category/FormCategory.vue'; import FormCategory from '../../classes/field-types/category/FormCategory.vue';
import FormSelectbox from '../../classes/field-types/selectbox/FormSelectbox.vue';
import TaincanFormItem from '../../classes/field-types/tainacan-form-item.vue'; import TaincanFormItem from '../../classes/field-types/tainacan-form-item.vue';
@ -43,8 +44,10 @@ Vue.component('tainacan-radio', Radio);
Vue.component('tainacan-numeric', Numeric); Vue.component('tainacan-numeric', Numeric);
Vue.component('tainacan-date', Date); Vue.component('tainacan-date', Date);
Vue.component('tainacan-relationship', Relationship); Vue.component('tainacan-relationship', Relationship);
Vue.component('tainacan-form-relationship', FormRelationship); Vue.component('tainacan-form-relationship', FormRelationship);
Vue.component('tainacan-form-category', FormCategory); Vue.component('tainacan-form-category', FormCategory);
Vue.component('tainacan-form-selectbox', FormSelectbox);
Vue.component('tainacan-form-item', TaincanFormItem); Vue.component('tainacan-form-item', TaincanFormItem);
Vue.component('draggable', draggable); Vue.component('draggable', draggable);

View File

@ -85,6 +85,12 @@ return [
'label_collection_related' => __('Collection Related', 'tainacan'), 'label_collection_related' => __('Collection Related', 'tainacan'),
'label_fields_for_search' => __('Fields for search', 'tainacan'), 'label_fields_for_search' => __('Fields for search', 'tainacan'),
'label_allow_repeated_items' => __('Allow repeated items', 'tainacan'), 'label_allow_repeated_items' => __('Allow repeated items', 'tainacan'),
'label_select_category' => __('Select category', 'tainacan'),
'label_select_category_input_type' => __('Input type', 'tainacan'),
'label_category_allow_new_terms' => __('Allow new terms', 'tainacan'),
'label_selectbox_init' => __('Select', 'tainacan'),
'label_options' => __('Insert options', 'tainacan'),
'label_attachments' => __('Attachments', 'tainacan'),
// Instructions. More complex sentences to guide user and placeholders // Instructions. More complex sentences to guide user and placeholders
'instruction_dragndrop_fields_collection' => __('Drag and drop Fields here to add them to Collection.', 'tainacan'), 'instruction_dragndrop_fields_collection' => __('Drag and drop Fields here to add them to Collection.', 'tainacan'),

View File

@ -1,13 +1,19 @@
<template> <template>
<section> <section
<b-field label="Category"> v-if="isReady"
:listen="setError">
<b-field :label="$i18n.get('label_select_category')"
:type="taxonomyType"
:message="taxonomyMessage"
>
<b-select <b-select
name="field_type_options[taxonomy_id]" name="field_type_options[taxonomy_id]"
placeholder="Select the taxonomy" placeholder="Select the taxonomy"
v-model="taxonomy_id" v-model="taxonomy_id"
@change.native="emitValues()" @input="emitValues()"
@focus="clear"
:loading="loading"> :loading="loading">
<option value="">Select...</option> <option value="">{{ $i18n.get('label_selectbox_init') }}...</option>
<option <option
v-for="option in taxonomies" v-for="option in taxonomies"
:value="option.id" :value="option.id"
@ -17,18 +23,19 @@
</b-select> </b-select>
</b-field> </b-field>
<b-field label="Input Type"> <b-field :label="$i18n.get('label_select_category_input_type')">
<b-select <b-select
v-if="listInputType()" v-if="listInputType"
name="field_type_options[component_type]" name="field_type_options[component_type]"
placeholder="Select the input type for the category field" placeholder="Select the input type for the category field"
@input="emitValues()"
v-model="input_type"> v-model="input_type">
<option :value="'tainacan-category-radio'"> <option
Radio v-for="option,index in single_types"
</option> :value="index"
<option :value="'tainacan-category-selectbox'"> :key="index">
Selectbox {{ option }}
</option> </option>
</b-select> </b-select>
@ -36,22 +43,23 @@
name="field_type_options[input_type]" name="field_type_options[input_type]"
placeholder="Select the input type for the category field" placeholder="Select the input type for the category field"
v-model="input_type" v-model="input_type"
@input="emitValues()"
v-else> v-else>
<option :value="'tainacan-category-checkbox'"> <option
Checkbox v-for="option,index in multiple_types"
</option> :value="index"
<option :value="'tainacan-category-tag-input'" > :key="index">
Tag Input {{ option }}
</option> </option>
</b-select> </b-select>
</b-field> </b-field>
<b-field label="Allow new Terms"> <b-field :label="$i18n.get('label_category_allow_new_terms')">
<div class="block"> <div class="block">
<b-switch v-model="allow_new_terms" <b-switch v-model="allow_new_terms"
type="is-info" type="is-primary"
@input="emitValues()" @input="emitValues()"
true-value="yes" true-value="yes"
false-value="no"> false-value="no">
@ -64,12 +72,13 @@
</template> </template>
<script> <script>
import axios from '../../../js/axios/axios'; import { tainacan as axios } from '../../../js/axios/axios';
export default { export default {
props: { props: {
value: [ String, Object, Array ], value: [ String, Object, Array ],
field: [ String, Object ] field: [ String, Object ],
errors: [ String, Object, Array ]
}, },
created(){ created(){
this.fetchTaxonomies().then( res => { this.fetchTaxonomies().then( res => {
@ -80,28 +89,54 @@
if( this.value ) { if( this.value ) {
this.allow_new_terms = ( this.value.allow_new_terms ) ? this.value.allow_new_terms : 'no'; this.allow_new_terms = ( this.value.allow_new_terms ) ? this.value.allow_new_terms : 'no';
this.input_type = ( this.value.input_type ) ? this.value.input_type : this.listInputType(); }
this.single_types['tainacan-category-radio'] = 'Radio';
this.single_types['tainacan-category-selectbox'] = 'Selectbox';
this.multiple_types['tainacan-category-tag-input'] = 'Tag Input';
this.multiple_types['tainacan-category-checkbox'] = 'Checkbox';
this.isReady = true;
},
computed: {
listInputType(){
if( this.field && this.field.multiple === 'no' ){
let types = Object.keys( this.single_types );
let hasValue = this.value && this.value.input_type && types.indexOf( this.value.input_type ) >= 0;
this.input_type = ( hasValue ) ? this.value.input_type : 'tainacan-category-radio';
return true;
} else {
let types = Object.keys( this.multiple_types );
let hasValue = this.value && this.value.input_type && types.indexOf( this.value.input_type ) >= 0;
this.input_type = ( hasValue ) ? this.value.input_type : 'tainacan-category-checkbox';
return false;
}
},
setError(){
if( this.errors && this.errors.taxonomy_id !== '' ){
this.taxonomyType = 'is-warning';
this.taxonomyMessage = this.errors.taxonomy_id;
} else {
this.taxonomyType = '';
this.taxonomyMessage = '';
}
} }
}, },
data(){ data(){
return { return {
isReady: false,
taxonomies: [], taxonomies: [],
taxonomy_id: '', taxonomy_id: '',
loading: true, loading: true,
allow_new_terms: 'yes', allow_new_terms: 'yes',
input_type: 'tainacan-category-radio' input_type: 'tainacan-category-radio',
multiple_types: {},
single_types: {},
taxonomyType:'',
taxonomyMessage: ''
} }
}, },
methods: { methods: {
listInputType(){
if( this.field && this.field.multiple === 'no' ){
this.input_type = 'tainacan-category-radio';
return true;
} else {
this.input_type = 'tainacan-category-checkbox';
return false;
}
},
fetchTaxonomies(){ fetchTaxonomies(){
return axios.get('/taxonomies') return axios.get('/taxonomies')
.then(res => { .then(res => {
@ -122,6 +157,10 @@
labelNewTerms(){ labelNewTerms(){
return ( this.allow_new_terms === 'yes' ) ? this.$i18n.get('label_yes') : this.$i18n.get('label_no'); return ( this.allow_new_terms === 'yes' ) ? this.$i18n.get('label_yes') : this.$i18n.get('label_no');
}, },
clear(){
this.taxonomyType = '';
this.taxonomyMessage = '';
},
emitValues(){ emitValues(){
this.$emit('input',{ this.$emit('input',{
taxonomy_id: this.taxonomy_id, taxonomy_id: this.taxonomy_id,

View File

@ -100,7 +100,8 @@ class Category extends Field_Type {
if( is_array( $category_fields ) ){ if( is_array( $category_fields ) ){
foreach ($category_fields as $field_id => $category_field) { foreach ($category_fields as $field_id => $category_field) {
if ( $field_id != $field->get_id() && in_array($this->get_option('taxonomy_id'), $category_fields)) { if ( is_array( $category_field ) && key($category_field) != $field->get_id()
&& in_array($this->get_option('taxonomy_id'), $category_field)) {
return ['taxonomy_id' => __('You can not have 2 Category Fields using the same category in a collection', 'tainacan')]; return ['taxonomy_id' => __('You can not have 2 Category Fields using the same category in a collection', 'tainacan')];
} }
} }

View File

@ -2,7 +2,7 @@
<section> <section>
<b-field <b-field
:label="$i18n.get('label_collection_related')" :label="$i18n.get('label_collection_related')"
:listen="setError()" :listen="setError"
:type="collectionType" :type="collectionType"
:message="collectionMessage"> :message="collectionMessage">
<b-select <b-select
@ -61,7 +61,7 @@
</template> </template>
<script> <script>
import axios from '../../../js/axios/axios'; import { tainacan as axios } from '../../../js/axios/axios';
import Vue from 'vue'; import Vue from 'vue';
export default { export default {
@ -83,7 +83,9 @@
hasFields: false, hasFields: false,
loadingFields: false, loadingFields: false,
modelRepeated: 'yes', modelRepeated: 'yes',
modelSearch:[] modelSearch:[],
collectionType: '',
collectionMessage: ''
} }
}, },
watch:{ watch:{
@ -117,6 +119,17 @@
this.modelRepeated = this.value.repeated; this.modelRepeated = this.value.repeated;
} }
}, },
computed: {
setError(){
if( this.errors && this.errors.collection_id !== '' ){
this.collectionType = 'is-warning';
this.collectionMessage = this.errors.collection_id;
} else {
this.collectionType = '';
this.collectionMessage = '';
}
},
},
methods:{ methods:{
fetchCollections(){ fetchCollections(){
return axios.get('/collections') return axios.get('/collections')
@ -185,20 +198,9 @@
labelRepeated(){ labelRepeated(){
return ( this.modelRepeated === 'yes' ) ? this.$i18n.get('label_yes') : this.$i18n.get('label_no'); return ( this.modelRepeated === 'yes' ) ? this.$i18n.get('label_yes') : this.$i18n.get('label_no');
}, },
setError(){
if( this.errors && this.errors.collection_id !== '' ){
this.collectionType = 'is-warning';
this.collectionMessage = this.errors.collection_id;
} else {
this.collectionType = '';
this.collectionMessage = '';
}
},
clear(){ clear(){
if( this.errors && this.errors.collection_id ){ this.collectionType = '';
this.errors.collection_id = ''; this.collectionMessage = '';
}
}, },
emitValues(){ emitValues(){
this.$emit('input',{ this.$emit('input',{

View File

@ -114,11 +114,11 @@ class Relationship extends Field_Type {
if (!empty($this->get_option('collection_id')) && !is_numeric($this->get_option('collection_id'))) { if (!empty($this->get_option('collection_id')) && !is_numeric($this->get_option('collection_id'))) {
return [ return [
'collection_id' => 'Collection ID invalid' 'collection_id' => __('Collection ID invalid','tainacan')
]; ];
} else if( empty($this->get_option('collection_id'))) { } else if( empty($this->get_option('collection_id'))) {
return [ return [
'collection_id' => 'Collection related is required' 'collection_id' => __('Collection related is required','tainacan')
]; ];
} }
return true; return true;

View File

@ -0,0 +1,67 @@
<template>
<section
:listen="setError">
<b-field :label="$i18n.get('label_options')"
:type="optionType"
:message="optionMessage"
>
<b-taginput
v-model="options"
@input="emitValues()"
@focus="clear()"
icon="label"
:placeholder="$i18n.get('new')">
</b-taginput>
</b-field>
</section>
</template>
<script>
export default {
props: {
value: [ String, Object, Array ],
field: [ String, Object ],
errors: [ String, Object, Array ]
},
data() {
return {
optionType: '',
optionMessage: '',
options: []
}
},
created(){
if( this.value ) {
this.options = ( this.value.options ) ? this.value.options.split('\n') : [];
}
},
computed: {
setError(){
if( this.errors && this.errors.options !== '' ){
this.optionType = 'is-warning';
this.optionMessage = this.errors.options;
} else {
this.optionType = '';
this.optionMessage = '';
}
}
},
methods: {
clear(){
this.optionType = '';
this.optionMessage = '';
},
emitValues(){
this.$emit('input',{
options: ( this.options.length > 0 ) ? this.options.join('\n') : ''
})
}
}
}
</script>
<style scoped>
section{
margin-bottom: 10px;
}
</style>

View File

@ -14,6 +14,7 @@ class Selectbox extends Field_Type {
parent::__construct(); parent::__construct();
parent::set_primitive_type('string'); parent::set_primitive_type('string');
$this->component = 'tainacan-selectbox'; $this->component = 'tainacan-selectbox';
$this->form_component = 'tainacan-form-selectbox';
} }
/** /**
@ -47,4 +48,21 @@ class Selectbox extends Field_Type {
</tr> </tr>
<?php <?php
} }
/**
* @param \Tainacan\Entities\Field $field
* @return array|bool true if is validate or array if has error
*/
public function validate_options(\Tainacan\Entities\Field $field) {
if ( !in_array($field->get_status(), apply_filters('tainacan-status-require-validation', ['publish','future','private'])) )
return true;
if ( empty($this->get_option('options')) ) {
return [
'options' => __('Options is required','tainacan')
];
}
return true;
}
} }

View File

@ -87,7 +87,6 @@ $Tainacan_Fields->register_field_type('Tainacan\Field_Types\Date');
$Tainacan_Fields->register_field_type('Tainacan\Field_Types\Numeric'); $Tainacan_Fields->register_field_type('Tainacan\Field_Types\Numeric');
$Tainacan_Fields->register_field_type('Tainacan\Field_Types\Selectbox'); $Tainacan_Fields->register_field_type('Tainacan\Field_Types\Selectbox');
$Tainacan_Fields->register_field_type('Tainacan\Field_Types\Relationship'); $Tainacan_Fields->register_field_type('Tainacan\Field_Types\Relationship');
$Tainacan_Fields->register_field_type('Tainacan\Field_Types\Radio');
$Tainacan_Fields->register_field_type('Tainacan\Field_Types\Category'); $Tainacan_Fields->register_field_type('Tainacan\Field_Types\Category');
global $Tainacan_Filters; global $Tainacan_Filters;

View File

@ -1,12 +1,12 @@
import axios from 'axios'; import axios from 'axios';
const tainacan = axios.create({ export const tainacan = axios.create({
baseURL: tainacan_plugin.root baseURL: tainacan_plugin.root
}); });
tainacan.defaults.headers.common['X-WP-Nonce'] = tainacan_plugin.nonce; tainacan.defaults.headers.common['X-WP-Nonce'] = tainacan_plugin.nonce;
const wp = axios.create({ export const wp = axios.create({
baseURL: tainacan_plugin.root_wp_api baseURL: tainacan_plugin.root_wp_api
}); });

View File

@ -194,7 +194,7 @@ class Fields extends TAINACAN_UnitTestCase {
*/ */
function test_metadata_field_type(){ function test_metadata_field_type(){
global $Tainacan_Fields; global $Tainacan_Fields;
$this->assertEquals( 8, sizeof( $Tainacan_Fields->fetch_field_types() ) ); $this->assertEquals( 7, sizeof( $Tainacan_Fields->fetch_field_types() ) );
} }
@ -354,6 +354,7 @@ class Fields extends TAINACAN_UnitTestCase {
$invalidField->set_name('test'); $invalidField->set_name('test');
$invalidField->set_description('test'); $invalidField->set_description('test');
$invalidField->set_collection($collection); $invalidField->set_collection($collection);
$invalidField->set_status('publish');
$invalidField->set_field_type('Tainacan\Field_Types\Relationship'); $invalidField->set_field_type('Tainacan\Field_Types\Relationship');
$invalidField->set_field_type_options(['collection_id' => 'string']); $invalidField->set_field_type_options(['collection_id' => 'string']);