diff --git a/lib/app/base-chips/field-types/field-types.test.js b/lib/app/base-chips/field-types/field-types.test.js --- a/lib/app/base-chips/field-types/field-types.test.js +++ b/lib/app/base-chips/field-types/field-types.test.js @@ -1,3 +1,4 @@ describe("field types", () => { require("./single_reference.subtest.js"); + require("./text.subtest.js"); }); diff --git a/lib/app/base-chips/field-types/text.js b/lib/app/base-chips/field-types/text.js --- a/lib/app/base-chips/field-types/text.js +++ b/lib/app/base-chips/field-types/text.js @@ -4,95 +4,119 @@ module.exports = { name: "text", - has_index: function(params){ - if(params.full_text_search || params.include_in_search){ - return {original: "text"}; - }else{ + has_index: function(params) { + if (params.full_text_search || params.include_in_search) { + return { original: "text" }; + } else { return false; } }, - get_description: function(context, params){ + get_description: function(context, params) { return `Text with maximum length ${params.max_length}`; }, - is_proper_value: function(context, params, new_value){ - const checks = []; - if(params.min_length){ - checks.push((text) => { - if(text.length < params.min_length){ - return Promise.reject(`Text '${new_value}' is too short, minimum length is ${params.min_length} chars.`); + is_proper_value: function(context, params, new_value) { + let checks = []; + + checks.push(text => { + if (typeof new_value !== "string") { + return Promise.reject( + `Type of ${new_value} is ${typeof new_value}, not string.` + ); + } + }); + if (params.min_length) { + checks.push(text => { + if (text.length < params.min_length) { + return Promise.reject( + `Text '${new_value}' is too short, minimum length is ${ + params.min_length + } chars.` + ); } }); } - if(params.max_length){ - checks.push((text) => { - if(text.length > params.max_length){ - return Promise.reject(`Text '${new_value}' has exceeded max length of ${params.max_length} chars`); + if (params.max_length) { + checks.push(text => { + if (text.length > params.max_length) { + return Promise.reject( + `Text '${new_value}' has exceeded max length of ${ + params.max_length + } chars.` + ); } }); } - return Promise.all(checks.map(fn => fn(new_value))) - .then(() => Promise.resolve()); + return Promise.all(checks.map(fn => fn(new_value))).then(() => + Promise.resolve() + ); }, - encode: function(context, params, value_in_code){ - if (typeof value_in_code === "string" && value_in_code !== null){ + encode: function(context, params, value_in_code) { + if (typeof value_in_code === "string" && value_in_code !== null) { const result = { - "original": value_in_code, - "safe": escape(value_in_code), - valueOf: function(){ + original: value_in_code, + safe: escape(value_in_code), + valueOf: function() { return value_in_code; }, }; return Promise.resolve(result); - } else { - return Promise.resolve(null); - } + } else return Promise.resolve(null); }, - get_aggregation_stages: function(context, params, field_name, query_params){ - const filter_value = query_params.filter && query_params.filter[field_name]; - if(filter_value){ - if(filter_value.regex){ + get_aggregation_stages: function( + context, + params, + field_name, + query_params + ) { + const filter_value = + query_params.filter && query_params.filter[field_name]; + if (filter_value) { + if (filter_value.regex) { const regex_options = "i"; - const regex_query = {$regex: filter_value.regex, $options: regex_options}; + const regex_query = { + $regex: filter_value.regex, + $options: regex_options, + }; return [ { $match: { $or: [ { - [`body.${ field_name }.original`]: regex_query, + [`body.${field_name}.original`]: regex_query, }, { - [`body.${ field_name }.safe`]: regex_query, - } - ] + [`body.${field_name}.safe`]: regex_query, + }, + ], }, }, ]; - }else{ - const value_query = {$eq: filter_value}; + } else { + const value_query = { $eq: filter_value }; return [ { $match: { $or: [ { - [`body.${ field_name }.original`]: filter_value, + [`body.${field_name}.original`]: filter_value, }, { - [`body.${ field_name }.safe`]: filter_value, - } - ] + [`body.${field_name}.safe`]: filter_value, + }, + ], }, }, ]; } - }else{ + } else { return []; } }, - format: function(context, params, decoded_value, format){ - if(decoded_value === null || decoded_value === undefined){ + format: function(context, params, decoded_value, format) { + if (decoded_value === null || decoded_value === undefined) { return Promise.resolve(decoded_value); } - if(format === undefined){ + if (format === undefined) { return decoded_value.safe; } return decoded_value[format] ? decoded_value[format] : decoded_value; diff --git a/lib/app/base-chips/field-types/text.subtest.js b/lib/app/base-chips/field-types/text.subtest.js new file mode 100644 --- /dev/null +++ b/lib/app/base-chips/field-types/text.subtest.js @@ -0,0 +1,86 @@ +const assert = require("assert"); +const locreq = require("locreq")(__dirname); +const axios = require("axios"); +const with_test_app = locreq("test_utils/with-test-app.js"); + +describe("text", () => { + const COLLECTION_NAME = "surnames"; + + async function create_test_collection({ app, params }) { + app.createChip(app.Sealious.Collection, { + name: COLLECTION_NAME, + fields: [ + { + name: "surname", + type: "text", + params, + }, + ], + }); + } + + function assert_creation_error_factory({ base_url, collection }) { + return async ({ resource, message }) => { + try { + await axios.post( + `${base_url}/api/v1/collections/${collection}`, + resource + ); + throw "This should not pass"; + } catch (e) { + assert.deepEqual(e.response.data.data.surname.message, message); + } + }; + } + + it("shouldn't allow a value that isn't a string", async () => + with_test_app(async ({ app, base_url }) => { + await create_test_collection({ app, params: {} }); + const assert_creation_error = assert_creation_error_factory({ + base_url, + collection: COLLECTION_NAME, + }); + await assert_creation_error({ + resource: { surname: false }, + message: "Type of false is boolean, not string.", + }); + await assert_creation_error({ + resource: { surname: {} }, + message: "Type of [object Object] is object, not string.", + }); + })); + + it("should respect given min and max length", async () => + with_test_app(async ({ app, base_url }) => { + await create_test_collection({ + app, + params: { min_length: 3, max_length: 5 }, + }); + const assert_creation_error = assert_creation_error_factory({ + base_url, + collection: COLLECTION_NAME, + }); + await assert_creation_error({ + resource: { surname: "lo" }, + message: "Text 'lo' is too short, minimum length is 3 chars.", + }); + await assert_creation_error({ + resource: { surname: "abcdefghijk" }, + message: + "Text 'abcdefghijk' has exceeded max length of 5 chars.", + }); + })); + + it("should let proper string in", async () => + with_test_app(async ({ app, base_url }) => { + await create_test_collection({ + app, + params: { min_length: 3, max_length: 5 }, + }); + return axios + .post(`${base_url}/api/v1/collections/surnames`, { + surname: "1234", + }) + .then(resp => assert.deepEqual(resp.status, 201)); + })); +});