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 --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
Cypress.Commands.add('loginByForm', (username, password) => {
Cypress.log({
name: 'loginByForm',
name: 'loginByRequest',
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({
method: 'POST',
url: '/login',
form: true,
body: {
log: username,
pwd: password
log: 'admin',
pwd: 'admin'
}
})
// we should be redirected to /wp-admin
cy.url().should('include', '/wp-admin')
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 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';
@ -43,8 +44,10 @@ Vue.component('tainacan-radio', Radio);
Vue.component('tainacan-numeric', Numeric);
Vue.component('tainacan-date', Date);
Vue.component('tainacan-relationship', Relationship);
Vue.component('tainacan-form-relationship', FormRelationship);
Vue.component('tainacan-form-category', FormCategory);
Vue.component('tainacan-form-selectbox', FormSelectbox);
Vue.component('tainacan-form-item', TaincanFormItem);
Vue.component('draggable', draggable);

View File

@ -85,6 +85,12 @@ return [
'label_collection_related' => __('Collection Related', 'tainacan'),
'label_fields_for_search' => __('Fields for search', '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
'instruction_dragndrop_fields_collection' => __('Drag and drop Fields here to add them to Collection.', 'tainacan'),

View File

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

View File

@ -100,7 +100,8 @@ class Category extends Field_Type {
if( is_array( $category_fields ) ){
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')];
}
}

View File

@ -2,7 +2,7 @@
<section>
<b-field
:label="$i18n.get('label_collection_related')"
:listen="setError()"
:listen="setError"
:type="collectionType"
:message="collectionMessage">
<b-select
@ -61,7 +61,7 @@
</template>
<script>
import axios from '../../../js/axios/axios';
import { tainacan as axios } from '../../../js/axios/axios';
import Vue from 'vue';
export default {
@ -83,7 +83,9 @@
hasFields: false,
loadingFields: false,
modelRepeated: 'yes',
modelSearch:[]
modelSearch:[],
collectionType: '',
collectionMessage: ''
}
},
watch:{
@ -117,6 +119,17 @@
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:{
fetchCollections(){
return axios.get('/collections')
@ -185,20 +198,9 @@
labelRepeated(){
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 {
clear(){
this.collectionType = '';
this.collectionMessage = '';
}
},
clear(){
if( this.errors && this.errors.collection_id ){
this.errors.collection_id = '';
}
},
emitValues(){
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'))) {
return [
'collection_id' => 'Collection ID invalid'
'collection_id' => __('Collection ID invalid','tainacan')
];
} else if( empty($this->get_option('collection_id'))) {
return [
'collection_id' => 'Collection related is required'
'collection_id' => __('Collection related is required','tainacan')
];
}
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::set_primitive_type('string');
$this->component = 'tainacan-selectbox';
$this->form_component = 'tainacan-form-selectbox';
}
/**
@ -47,4 +48,21 @@ class Selectbox extends Field_Type {
</tr>
<?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\Selectbox');
$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');
global $Tainacan_Filters;

View File

@ -1,12 +1,12 @@
import axios from 'axios';
const tainacan = axios.create({
export const tainacan = axios.create({
baseURL: tainacan_plugin.root
});
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
});

View File

@ -194,7 +194,7 @@ class Fields extends TAINACAN_UnitTestCase {
*/
function test_metadata_field_type(){
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_description('test');
$invalidField->set_collection($collection);
$invalidField->set_status('publish');
$invalidField->set_field_type('Tainacan\Field_Types\Relationship');
$invalidField->set_field_type_options(['collection_id' => 'string']);