test/unit/services.content.js
import test from 'ava';
import nock from 'nock';
import {
client,
clinical6,
contentService,
statusService,
Content,
ContentType,
Query,
} from '../../src';
test.before('start server', (t) => {
client.apiBaseUrl = 'https://somesite.Clinical6.com';
const types = [
new ContentType({
id: '76',
type: 'dynamic_content__content_types',
attributes: {
name: 'dummy_407',
permanent_link: 'home_health_visit',
description: null,
created_at: '2017-07-28T15:30:39Z',
updated_at: '2017-07-28T15:30:39Z'
}
}),
new ContentType({
id: '182',
type: 'dynamic_content__content_types',
attributes: {
name: 'Mayor and Council',
permanent_link: 'mayor_and_council',
description: null,
created_at: '2017-07-28T15:30:40Z',
updated_at: '2017-07-28T15:30:40Z'
}
}),
new ContentType({
id: '183',
type: 'dynamic_content__content_types',
attributes: {
name: 'Mayor and Council',
permanent_link: 'acronyms',
description: null,
created_at: '2017-07-28T15:30:40Z',
updated_at: '2017-07-28T15:30:40Z'
}
})
];
types.forEach(type => type.store());
t.context.filterResponse = {
status: 'ok',
content: [
{
created_at: '2014-09-11T00:05:18Z',
description: 'Mayoral Description blah blah blah',
id: 1,
image: {
url: 'https://captivereach-dhsadobe.s3.amazonaws.com/1/mayor_1.jpg'
},
position: 1,
title: 'R. Scott Silverthorne',
brand: {
ancestry: null,
created_at: '2014-09-10T01:51:38Z',
description: null,
id: 1,
logo: {
url: '/assets/default/default-b62e7263fe5a6d7bd06d942b3702ac2f.png'
},
name: 'fairfax',
position: 1,
updated_at: '2014-09-10T01:51:38Z'
},
title_legend: 'Serving his second term as Mayor.',
email: 'Scott.silverthorne@fairfaxva.gov',
rank: 'Mayor',
phone: '703-385-7800',
website: 'http://www.fairfaxva.gov/government/mayor/r-scott-silverthorne',
email2: 'cityclerk@fairfaxva.gov',
content_type: 'mayor_and_council'
}
],
total_pages: 2
};
t.context.filterResponseCustomBands = {
content: [{
id: 65,
title: 'School of Rock',
tags: [{
created_at: '2014-09-13T00:14:59Z',
id: 3,
name: 'Children’s Stage',
position: 3,
updated_at: '2014-09-13T00:14:59Z'
}],
content_type: 'bands'
}, {
id: 66,
title: 'Turley the Magician',
tags: [{
created_at: '2014-09-13T00:14:59Z',
id: 3,
name: 'Children’s Stage',
position: 3,
updated_at: '2014-09-13T00:14:59Z'
}],
content_type: 'bands'
}, {
id: 67,
title: 'Chuck F. - Juggler',
tags: [{
created_at: '2014-09-13T00:14:59Z',
id: 3,
name: 'Children’s Stage',
position: 3,
updated_at: '2014-09-13T00:14:59Z'
}],
content_type: 'bands'
}],
total_pages: 1
};
t.context.getPublicContents = {
data: [
{
id: '36',
type: 'dynamic_content__contents',
attributes: {
title: 'Terms of Use',
description: '<h2>Clinical6 Terms of Use</h2><br/> <br/>Last Updated: April 2018<br/>PLEASE READ THE FOLLOWING TERMS OF USE BEFORE USING the Clinical6 Mobile application (the “App”). All users of the App are subject to the following terms and conditions under applicable law. If you do not agree to these terms and conditions, please do not use this App. <br/>Parallel6, Inc. (“Parallel6”, “we,” or “us”) has developed this App, which is owned and controlled by the Sponsor, to facilitate the conduct of a clinical trial (“Study”) by the Sponsor to whom Parallel6 provides Study-related services. <br/><br/>Eligibility<br/>The App and the services found on this App are available only to users who can form legally binding contracts under applicable law and who are participants in a Study or who have otherwise been granted access to the App by Parallel6 or a Sponsor through the App’s registration process. By using the App, you represent and warrant that you (i) are at least eighteen (18) years of age and/or are otherwise recognized as being able to form legally binding contracts under applicable law; and (ii) have provided accurate, current, and complete information in connection with your registration for the App.<br/><br/>Medical Disclaimer<br/>The App does not provide medical advice. The information contained in the App is not a substitute for medical advice or treatment. Please consult with your doctor for medical advice or treatment or with any questions or concerns you may have regarding your individual needs or medical conditions or any questions about the Study in which you may be participating. <br/><br/>Consent to Use of Data<br/>You agree that Parallel6 and the Sponsor may collect, store, process, maintain, upload, sync, transmit, share, disclose and use certain data and information, including but not limited to information or data regarding the characteristics or usage of your mobile device, system and application software, and peripherals, as well as personal information, user location data and user content (including personal health information) (collectively, “User Data”) to facilitate the conduct of the Study and the provision of the services or functionality of the App, including but not limited to authentication, performance optimization, software updates, product support and other services related to the App or the Study or to otherwise improve Parallel6’s ability to provide other services (if any) to you or to the Sponsor related to the App. You acknowledge and agree that (a) use of the App may result in User Data being transmitted between your device and systems operated by Parallel6 and/or the Sponsor and that User Data may be used to make changes, updates or improvements to or optimize the performance of the App or to otherwise inform future development; and (b) audit logs reflecting your logins, logouts and the activities you have accessed through your use of the App may be generated in connection with your use of the App and may be collected, transmitted and stored on systems operated by Parallel6 or the Sponsor. By continuing to use the App, you indicate your continued consent to such collection, storage, processing, maintenance, uploading, syncing, transmitting, sharing, or disclosure of User Data as well as collection, storage, transmission and use of data of the type and in the manner described in the Clinical6 Privacy Policy.<br/><br/>Copyright<br/>Permission is granted to electronically copy and print hard copy portions of the App for the sole purpose of utilizing the App. Any other use, including but not limited to the reproduction, distribution, display, or transmission of the content of the App is strictly prohibited, unless authorized by Parallel6. You further agree not to change or delete any proprietary notices from materials associated with the App.<br/><br/>Trademarks<br/>All trademarks, service marks, and trade names of Parallel6, affiliates and program partners used in the App are owned and copyrighted by Parallel6 or its program partners or vendors and are protected by U.S. and international copyright laws.<br/><br/>Any use of the App or its content that is not expressly permitted by these Terms of Use is a breach of this Agreement and may violate copyright, trademark, and other laws. All rights not expressly granted to you in this Agreement are reserved by us and our licensors.<br/><br/>Warranty Disclaimer<br/>The App and the materials and products in the App are provided "as is" and without warranty of any kind, whether express or implied.<br/><br/>Limitation of Liability<br/>Parallel6 shall not be liable for any special or consequential damages that result from the use of, or the inability to use, the services offered in the App, or the performance of the services. <br/><br/>Term; Termination<br/>These terms and conditions are applicable to you upon your accessing the App. These terms and conditions, or any part of them, may be terminated by Parallel6 without notice at any time, for any reason. The provisions relating to Consent to Use of Data, Medical Disclaimer, Copyrights, Trademark, Participation Disclaimer, Limitation of Liability, Indemnification and Miscellaneous, shall survive any termination.<br/><br/>Use of Mobile App<br/>Harassment in any manner or form on the App, including messaging received or sent by companion apps, or by use of obscene or abusive language, is strictly forbidden. Impersonation of others, including a Parallel6 or other licensed employee, host, or representative, as well as other members or visitors on the App is prohibited. You may not upload to, distribute, or otherwise publish through the App any content which is libelous, defamatory, obscene, threatening, invasive of privacy or publicity rights, abusive, illegal, or otherwise objectionable which may constitute or encourage a criminal offense, violate the rights of any party or which may otherwise give rise to liability or violate any law.<br/><br/>Participation Disclaimer<br/>Parallel6 does not and cannot review all communications and materials posted to or created by users accessing the App and is not in any manner responsible for the content of these communications and materials. You acknowledge that by providing you with the ability to view and distribute user-generated content on the App and any companion apps, Parallel6 is merely acting as a passive conduit for such distribution and is not undertaking any obligation or liability relating to any contents or activities on the App. However, Parallel6 reserves the right to block or remove communications or materials that it determines to be (a) abusive, defamatory, or obscene, (b) fraudulent, deceptive, or misleading, (c) in violation of a copyright, trademark or; other intellectual property right of another or (d) offensive or otherwise unacceptable to Parallel6 in its sole discretion.<br/><br/>Indemnification<br/>You agree to indemnify, defend, and hold harmless Parallel6, its officers, directors, employees, agents, licensors and suppliers (collectively the "Service Providers") and its program partners from and against all losses, expenses, damages and costs, including reasonable attorneys\' fees, resulting from any material breach of these terms and conditions or any activity related to your account by you or any other person accessing the App using your Internet account, except due to our gross negligence or wrongful misconduct.<br/><br/>Contact Information<br/>All notices to you relating to this Agreement shall be posted on the App or sent to you at the email address you provided. You will need access to a PDF reader to review and print this electronic record.<br/> <br/>If you have created an account and would like to update the contact information you have provided to us, you must contact your trial administrator to do so.<br/><br/><br/>You may contact us any time regarding these Terms of Use by writing to: <br/>Parallel6, Inc.<br/>1455 Frazee Road<br/>Suite 900<br/>San Diego, CA 92108<br/>USA<br/> <br/>For technical issues related to The App or clinical issues related to The Study, please contact your study technical help desk or study clinical representative, respectively, through the channels identified separately.',
image_url: '/assets/default/default-16daaa70b849a969f5ab41d3b7c9513c8ca890a6dd3b30e39c5afc25b8a773b6.png',
position: 2,
visibility_status: 'enabled',
created_at: '2018-07-25T21:02:13Z',
updated_at: '2019-03-06T21:33:05Z'
},
relationships: {
content_type: {
data: {
id: '2',
type: 'dynamic_content__content_types'
}
}
}
},
{
id: '43',
type: 'dynamic_content__contents',
attributes: {
title: 'Privacy Policy ',
description: 'Clinical6 Privacy Policy <br/><br/>Last Updated: March 2018<br/>Parallel6, Inc. (“Parallel6”, “we,” or “us”) is a software service provider of mobile and web-based enrollment, data collection, and engagement solutions for clinical research.<br/><br/>Parallel6 has developed Clinical6 web and mobile applications (the “App”) to facilitate the conduct of a clinical research study (the “Study”) by the Sponsor to whom Parallel6 provides Study-related services. <br/><br/>Parallel6 respects your privacy. This Privacy Policy describes our collection of personal data through the App, our use and disclosure of such personal data, and the steps we take to protect such personal data. As used in this Privacy Policy, the term “Personal Data” means any information that relates, directly or indirectly, to an identified or identifiable natural person. <br/><br/>This App is open for use by any participant in a Study who is over the age of 18 and who has successfully completed the App’s registration process, as well as by other individuals, such as friends and family members of a Study participant, who have been invited by the Study Participant to use the App as a member of the Study participant’s “Care Circle.”<br/><br/>It is important for you to understand while reviewing this Privacy Policy that in addition to Parallel6, the Sponsor, as well as participating institutions and investigators involved in the Study (“Study Sites”), will receive and use Personal Data we collect through the App. This Privacy Policy does not apply to the practices of the Sponsor or the Study Sites, or to any other organizations that we do not own or control. We encourage you to consult the privacy policies of these organizations, and to contact them separately if you have questions about their data handling practices. <br/><br/>Your use of the App is voluntary. By checking the box below that confirms you have read and agree to this Privacy Policy and clicking “Accept” during the registration process, you consent to our collection, transfer, storage, disclosure and use of your Personal Data as described in this Privacy Policy. This includes any information you choose to provide that is deemed sensitive under applicable law.<br/>Information We Collect and How We Use It<br/><br/>We collect and process three categories of information through the App: Registration Data, Study Data, and Usage Data. <br/><br/>Registration Data<br/>We collect certain basic information about you for Study qualification and/or registration purposes before you may access the App’s full functionality. This information is referred to as “Registration Data.” <br/><br/>If you successfully pre-qualify or are already enrolled in a Study, we will collect additional Personal Data for purposes of your registration for the App and the Study that may include your first and last name, your email address, your telephone number, your date of birth, your gender, and/or a numeric code provided to you during Study enrollment. <br/><br/>We use your Registration Data to determine whether you are eligible to participate in a Study, to enroll you as a participant in the Study, to manage your participation in the Study and use of the App, and to contact you regarding the Study and your use of the App. <br/><br/>Study Data<br/>You will be notified during the registration process of the specific items of information the App will collect for the Study in which you are participating. Study Data may include, but is not necessarily limited to, the categories described below.<br/>Third-Party Application and Device Imports. The App may allow you to import data that has been collected or made available by third-party medical or mHealth applications installed on your mobile device, or by devices such as wearable devices, glucometers, wireless scales, and pulse oximeters that connect to your mobile device. Such data may include sensitive health information. <br/>Surveys. We may collect and process some Personal Data, which may include sensitive health information, through surveys administered through the App that are necessary for the conduct of the Study and/or for the purpose of understanding your use of the App. Your participation in completing survey questions is important to the success of the Study. <br/>Location Data. With your permission, the App may collect information about your location to determine whether you are in a study center and confirm with you via alert notification. To enable the collection of location data you will need to turn on the location services on your phone when prompted by the App and grant the App access to your GPS coordinates. <br/>Other Information. We may also collect other information, which may include Personal Data, in connection with your use of the App or participation in the Study such as through comments or questions you submit to us. <br/>We use Study data for the purposes of facilitating the Study and to perform research and analysis in connection with the Study-related services that we provide to the Study Sponsor. Generally speaking, we replace information contained in Study Data that directly identifies you with a numeric code before we use your Study Data for these purposes. We may, however, re-identify Study Data if doing so is needed for research integrity or legal or regulatory purposes. <br/>In addition, we may aggregate coded Study Data with data collected from other Study participants and summarize it into broad (such as overall progress throughout the Study) or narrow (like responses to specific surveys) categories that will be used to improve patient engagement in future research studies.<br/><br/>Usage Data<br/>The App automatically collects certain technical information relating to your device and your use of the App that we may use to understand your interactions with the App and to customize and improve the App. This information is referred to as “Usage Data.” <br/>Usage Data may include the following categories of information:<br/>Device Parameters. The App automatically collects certain information about your mobile device, which may include UDID (Unique Device Identifier), technology platform (e.g, Android / iOS), App version, OS version, network status, brand, manufacturer, and carrier technology.<br/>Application Usage Log Information. We automatically collect and log certain details of your interactions with the App, such as your IP address and details of how you use the app (such as the history of page requests, data uploads, and sign-ins, along with the date and time of each). <br/>We use Usage Data to operate, maintain, and provide all features of the App and to provide user support. We also use Usage Data to allow the App to function effectively and to personalize the App experience, such as by remembering information so that it does not need to be re-entered. <br/>We may also use Usage Data to understand user experience and to develop improvements and enhancements to future versions of the App and other Parallel6 products and offerings.<br/><br/>How We Share Your Information<br/>Except as described in this Privacy Policy, we will not sell, rent, lease, give away, disclose or share Personal Data collected through the App to third parties without your consent. We may share your Personal Data with third parties if you consent to us doing so, as well as in the following circumstances:<br/>Registration Data and Study Data are collected on behalf of Sponsors and will be disclosed to Sponsors and Study Sites as part of our provision of services to them in connection with the Study.<br/>Your Registration Data and Study Data may be disclosed through the App to individuals, such as friends and family members, who you have invited to use the App as a member of your “Care Circle.”<br/>We may disclose Personal Data to third parties who provide services on our behalf in connection with our operation of the App and the Study (“Service Providers”). When we do so, we take steps to limit the Personal Data provided to the Service Provider to that which is reasonably necessary for them to perform their functions. We require Service Providers by contract to agree to only process the Personal Data we disclose in accordance with our instructions and to maintain the security and confidentiality of such Personal Data by applying adequate technical and organizational measures.<br/>To the extent required by applicable law, we may disclose Personal Data to comply with legal and regulatory requirements, including obligations to health and regulatory bodies. <br/>We also reserve the right to disclose any information that we believe, in good faith, is appropriate or necessary to (i) take precautions against liability, (ii) protect ourselves or others from fraudulent, abusive, or unlawful uses or activity, (iii) investigate and defend ourselves against any third-party claims or allegations, (iv) protect the security or integrity of the App and any facilities or equipment used to make the App available, or (v) protect our property or other legal rights (including, but not limited to, enforcement of our agreements), or the rights, property, or safety of others. We will notify you of any such disclosures.<br/>Information about our users, including Personal Data, may be disclosed and otherwise transferred to an acquirer, successor, or assignee as part of any merger, acquisition, debt financing, sale of assets, or similar transaction, or in the event of an insolvency, bankruptcy, or receivership in which information is transferred to one or more third parties as one of our business assets, to the extent and in the way as prescribed by applicable law.<br/><br/>Push Notifications<br/>“Push Notifications” are messages that are sent to your device by the App. The App may periodically send you Push Notifications to remind you of tasks to be completed or provide updates about the Study. <br/><br/>Do Not Track<br/>We do not track our App users over time and across third-party websites or online services to provide targeted advertising, and we do not specifically respond to Do Not Track (“DNT”) signals.<br/><br/>How We Secure Your Data<br/>We use physical, managerial, and technical safeguards that are reasonably designed to protect the confidentiality, integrity, and security of Personal Data that we collect and maintain against accidental or unlawful loss, theft and misuse and against unauthorized access, disclosure, alteration, or destruction. We cannot, however, fully guarantee the security of Personal Data or other information transmitted to us through the App. <br/>We take steps to de-identify Study Data collected through the App by removing Personal Data that reasonably can be used to identify you directly, such as your name, email address, or telephone number. However, experts in re-identification may be able to reverse our processes and/or attempt to re-identify you if given sufficient cross-reference information. Total anonymity cannot be guaranteed.<br/><br/>We will retain your Personal Data for as long as reasonably necessary for the purposes described above or as required by applicable law or our agreements with the Sponsor. Notwithstanding the foregoing, we may retain information in coded or anonymous form for statistical and research purposes for so long as we have a legitimate and lawful interest in doing so. <br/><br/>Your Rights and Choices<br/>You can choose not to provide certain kinds of Study Data in connection with your participation in the Study by de-activating the relevant settings within the App or your mobile device, such as for location data. You may also choose not to answer survey questions or upload images. Please note that failing to provide requested Study Data may result in you being limited or excluded from participation in the Study. It may also degrade the effectiveness of the App and your experience as a user. <br/><br/>Subject to applicable law, you may have certain rights regarding the Personal Data we maintain about you. You may have the right to request access to the Personal Data we maintain about you, or have your Personal Data rectified, blocked, or deleted if it is incorrect, inaccurate, or outdated. You may make such a request by contacting Parallel6 via the information provided below. We will respond to your request within a reasonable time frame as required by applicable law. Please note that while any changes we make to your Personal Data in response to a request will be reflected in active databases, we may as permitted by applicable law maintain a copy of the unrevised information in our records to ensure the integrity of the Study, for backup and archiving purposes, for prevention of fraud and abuse, to satisfy legal obligations, or where we otherwise believe that we have a legitimate reason to do so.<br/><br/>In some cases the Sponsor or the Study Site will be responsible for responding to your request. In that case, we will forward your request to the Sponsor or Study Site. <br/><br/>You can withdraw any consent you have provided to us for the processing of your Personal Data at any time and free of charge by contacting Parallel6 via the information provided below. Please note that if you withdraw your consent, we will stop collecting new data from you, but any coded Study Data that you have already provided may continue to be used in connection with the Study.<br/><br/>If you have questions about your rights and choices in relation to your Personal Data, or would like to request additional information, please contact PrivacyOfficer@prahs.com <br/><br/>Cross-Border Transfers of Your Information<br/>We may transfer your Personal Data to countries outside of your country of residence, which may have different personal data protection laws than the country in which you initially provided the information. In doing so, we comply with applicable legal requirements pertaining to the transfer of personal data to other countries and will protect that information as described in this Privacy Policy.<br/>Although you may access the App from a location outside of the United States, any Personal Data collected by us in connection with the App may be transferred to, processed, and stored within the United States. Also, we may transfer your data from the United States to other countries or regions in connection with our storage and processing of data, fulfilling your requests, and operating the App.<br/>During the registration process for the Study, you will be asked to consent to the transfer of your Personal Data, including your health information and location data, to countries outside of your country of residence, including to the United States.<br/><br/>Changes to Our Privacy Policy<br/>We may make changes to this Privacy Policy from time to time. The most current version of the Privacy Policy will be posted on this page with an updated revision date. If any changes to this Privacy Policy materially alter your rights or obligations under this Privacy Policy, we will make reasonable efforts to notify you of the change. For example, we may send a message to your email address, if we have one on file, or generate a Push Notification or similar notification when you access the App for the first time after such material changes are made. Your continued use of the App after the revised Privacy Policy has become effective indicates that you have read, understood and agreed to the then-current version of this Privacy Policy.<br/><br/>Contact<br/>If you have any questions, comments, or requests regarding this Privacy Policy or our processing of your information in the context of the Study, please contact: PrivacyOfficer@prahs.com. ',
image_url: '/assets/default/default-16daaa70b849a969f5ab41d3b7c9513c8ca890a6dd3b30e39c5afc25b8a773b6.png',
position: 2,
visibility_status: 'enabled',
created_at: '2018-07-25T21:02:16Z',
updated_at: '2019-03-06T21:33:05Z'
},
relationships: {
content_type: {
data: {
id: '3',
type: 'dynamic_content__content_types'
}
}
}
},
{
id: '35',
type: 'dynamic_content__contents',
attributes: {
title: 'Anti-Spam Policy',
description: 'Clinical6 Anti-Spam Policy <br/><br/>Last Updated: April 2018<br/><br/>Clinical6 (“Parallel6”, “we,” or “us”) is a software service provider of mobile enrollment & engagement solutions in clinical research. We are located in San Diego, CA. <br/>Parallel6 has developed this mobile App to facilitate the conduct of a clinical trial (the “Study”) by its sponsor. This App is open for use by any research study participant who has received access through the mobile App’s registration process.<br/><br/>It is important for you to note while reviewing this policy that the sponsor and Study sites will use the data collected through the mobile App. Data we collect through the mobile app is accessible to the sponsor and the Study sites for the purposes of conducting the Study.<br/><br/>In Contacting You<br/><br/>We use your contact information, including screen name and email address, to contact you for administrative purposes regarding your use of the App. <br/>We neither SPAM nor send bulk unsolicited emails. We take precautions to ensure that emails you receive from us are marked as legitimate emails that your internet service provider (ISP) will recognize as not having been modified by a third party in transit, thereby preventing these emails from being routed to SPAM folders. We use the DomainKeys Identified Mail (DKIM) standard to accomplish this level of assurance. <br/>Emails which you do receive from us are transactional (i.e., they are automatically sent or triggered) and based on actions you take. Such emails will Welcome you, Alert you, allow you to Reset Passwords, etc. from your real-time, on-demand requests. There is no need to unsubscribe to these types of emails as they are essentially “one-time” events. Only by withdrawing from the Study or use of the App, would you no longer receive these transactional emails.<br/><br/>To Contact UsIf you have any questions, comments or requests on this E-mail contact and Anti-Spam statement, please contact: PrivacyOfficer@praintl.com. ',
image_url: '/assets/default/default-16daaa70b849a969f5ab41d3b7c9513c8ca890a6dd3b30e39c5afc25b8a773b6.png',
position: 2,
visibility_status: 'enabled',
created_at: '2018-07-25T21:02:13Z',
updated_at: '2019-05-13T21:40:41Z'
},
relationships: {
content_type: {
data: {
id: '4',
type: 'dynamic_content__content_types'
}
}
}
}
],
included: [
{
id: '2',
type: 'dynamic_content__content_types',
attributes: {
name: 'terms of use',
permanent_link: 'terms_of_use',
description: '',
public: true,
created_at: '2018-07-18T22:18:59Z',
updated_at: '2018-07-25T21:02:13Z'
},
relationships: {
dynamic_attributes: {
data: []
}
}
},
{
id: '3',
type: 'dynamic_content__content_types',
attributes: {
name: 'privacy policy',
permanent_link: 'privacy_policy',
description: '',
public: true,
created_at: '2018-07-18T22:18:59Z',
updated_at: '2018-07-25T21:02:16Z'
},
relationships: {
dynamic_attributes: {
data: []
}
}
},
{
id: '4',
type: 'dynamic_content__content_types',
attributes: {
name: 'antispam policy',
permanent_link: 'antispam_policy',
description: '',
public: true,
created_at: '2018-07-18T22:18:59Z',
updated_at: '2018-07-25T21:02:13Z'
},
relationships: {
dynamic_attributes: {
data: []
}
}
}
]
};
t.context.getResponseId = {
data: {
id: 655256,
type: 'dynamic_content__contents',
attributes: {
created_at: '2016-05-12T22:06:38Z',
description: 'Workplace as a Service',
position: 2,
title: 'WPaaS',
tags: [],
content_type: 'acronyms'
},
relationships: {
content_type: {
data: {
id: '183',
type: 'dynamic_content__content_types'
}
}
}
}
};
t.context.getResponseTypesId = {
data: {
id: '76',
type: 'dynamic_content__content_types',
attributes: {
name: 'dummy_407',
permanent_link: 'home_health_visit',
description: null,
created_at: '2017-07-28T15:30:39Z',
updated_at: '2017-07-28T15:30:39Z'
}
},
};
t.context.getResponseTypesAll = {
data: [{
id: 76,
type: 'dynamic_content__content_types',
attributes: {
name: 'dummy_407',
description: null,
permanent_link: 'home_health_visit',
content_type: 'acronyms',
created_at: '2017-07-28T15:30:39Z',
updated_at: '2017-07-28T15:30:39Z'
}
},
{
id: '182',
type: 'dynamic_content__content_types',
attributes: {
name: 'Mayor and Council',
permanent_link: 'mayor_and_council',
description: null,
created_at: '2017-07-28T15:30:40Z',
updated_at: '2017-07-28T15:30:40Z'
}
},
{
id: '183',
type: 'dynamic_content__content_types',
attributes: {
name: 'Mayor and Council',
permanent_link: 'acronyms',
description: null,
created_at: '2017-07-28T15:30:40Z',
updated_at: '2017-07-28T15:30:40Z'
}
}
]
};
t.context.getResponseFavorites = {
status: 'ok',
content: [{
created_at: '2014-09-11T00:05:18Z',
id: 1,
position: 1,
title: 'R. Scott Silverthorne',
title_legend: 'Serving his second term as Mayor.',
email: 'Scott.silverthorne@fairfaxva.gov',
rank: 'Mayor',
website: 'http://www.fairfaxva.gov/government/mayor-city-council/mayor-r-scott-silverthorne',
content_type: 'mayor_and_council'
}, {
created_at: '2014-09-11T00:06:40Z',
id: 2,
position: 2,
title: 'Michael J. DeMarco',
title_legend: 'Serving his second term on City Council.',
email: 'Michael.DeMarco@fairfaxva.gov',
rank: 'Councilmember',
website: 'http://www.fairfaxva.gov/government/mayor-city-council/councilmember-michael-j-demarco',
content_type: 'mayor_and_council'
}, {
created_at: '2014-09-11T00:07:45Z',
id: 3,
position: 3,
title: 'Jeffrey C. Greenfield',
title_legend: 'Serving his eleventh term on City Council.',
email: 'Jeff.greenfield@fairfaxva.gov',
rank: 'Councilmember',
website: 'http://www.fairfaxva.gov/government/mayor-city-council/councilmember-jeffrey-c-greenfield',
content_type: 'mayor_and_council'
}],
total_pages: 1
};
t.context.getResponseRandom = {
status: 'ok',
content: {
created_at: '2016-05-12T22:06:38Z',
description: 'Workplace as a Service',
id: 655256,
position: 2,
title: 'WPaaS',
tags: [],
brand: {
ancestry: null,
created_at: '2014-09-10T01:51:38Z',
description: null,
id: 1,
logo: {
url: 'https://captivereach-dhsadobe.s3.amazonaws.com/uploads/staging/settings/us-dhs-logo.png',
small: {
url: 'https://captivereach-dhsadobe.s3.amazonaws.com/uploads/staging/settings/small_us-dhs-logo.png'
},
small_hd: {
url: 'https://captivereach-dhsadobe.s3.amazonaws.com/uploads/staging/settings/small_hd_us-dhs-logo.png'
},
fullscreen: {
url: 'https://captivereach-dhsadobe.s3.amazonaws.com/uploads/staging/settings/fullscreen_us-dhs-logo.png'
},
main: {
url: 'https://captivereach-dhsadobe.s3.amazonaws.com/uploads/staging/settings/main_us-dhs-logo.png'
},
fullscreen_hd: {
url: 'https://captivereach-dhsadobe.s3.amazonaws.com/uploads/staging/settings/fullscreen_hd_us-dhs-logo.png'
},
main_hd: {
url: 'https://captivereach-dhsadobe.s3.amazonaws.com/uploads/staging/settings/main_hd_us-dhs-logo.png'
},
iphone_4: {
url: 'https://captivereach-dhsadobe.s3.amazonaws.com/uploads/staging/settings/iphone_4_us-dhs-logo.png'
},
iphone_5: {
url: 'https://captivereach-dhsadobe.s3.amazonaws.com/uploads/staging/settings/iphone_5_us-dhs-logo.png'
},
galaxy_s3: {
url: 'https://captivereach-dhsadobe.s3.amazonaws.com/uploads/staging/settings/galaxy_s3_us-dhs-logo.png'
},
iphone_6: {
url: 'https://captivereach-dhsadobe.s3.amazonaws.com/uploads/staging/settings/iphone_6_us-dhs-logo.png'
},
ipad_non_retina: {
url: 'https://captivereach-dhsadobe.s3.amazonaws.com/uploads/staging/settings/ipad_non_retina_us-dhs-logo.png'
},
nexus_7_2012: {
url: 'https://captivereach-dhsadobe.s3.amazonaws.com/uploads/staging/settings/nexus_7_2012_us-dhs-logo.png'
},
iphone_6_plus: {
url: 'https://captivereach-dhsadobe.s3.amazonaws.com/uploads/staging/settings/iphone_6_plus_us-dhs-logo.png'
},
galaxy_s4: {
url: 'https://captivereach-dhsadobe.s3.amazonaws.com/uploads/staging/settings/galaxy_s4_us-dhs-logo.png'
},
nexus_7_2013: {
url: 'https://captivereach-dhsadobe.s3.amazonaws.com/uploads/staging/settings/nexus_7_2013_us-dhs-logo.png'
},
ipad_retina: {
url: 'https://captivereach-dhsadobe.s3.amazonaws.com/uploads/staging/settings/ipad_retina_us-dhs-logo.png'
},
nexus_10_2013: {
url: 'https://captivereach-dhsadobe.s3.amazonaws.com/uploads/staging/settings/nexus_10_2013_us-dhs-logo.png'
},
thumb: {
url: 'https://captivereach-dhsadobe.s3.amazonaws.com/uploads/staging/settings/thumb_us-dhs-logo.png'
}
},
name: 'fairfax',
position: 1,
updated_at: '2014-09-10T01:51:38Z'
},
content_type: 'permanent_link'
}
};
t.context.getResponseStatus = {
status: {
object: 'object',
type: 'type',
value: ''
}
};
t.context.getResponseVuforia = {
vuforia_id: '3769864600964fd4909323a64235e1be',
name: 'Test',
image: {
url: 'https://captivereach-crdemo.s3.amazonaws.com/uploads/production/vuforia_target/image/1/887766a119514ece8ed8cd2dbbc16254e3a9ba02.jpg',
small: {
url: 'https://captivereach-crdemo.s3.amazonaws.com/uploads/production/vuforia_target/image/1/small_887766a119514ece8ed8cd2dbbc16254e3a9ba02.jpg'
},
small_hd: {
url: 'https://captivereach-crdemo.s3.amazonaws.com/uploads/production/vuforia_target/image/1/small_hd_887766a119514ece8ed8cd2dbbc16254e3a9ba02.jpg'
},
fullscreen: {
url: 'https://captivereach-crdemo.s3.amazonaws.com/uploads/production/vuforia_target/image/1/fullscreen_887766a119514ece8ed8cd2dbbc16254e3a9ba02.jpg'
},
iphone_4: {
url: 'https://captivereach-crdemo.s3.amazonaws.com/uploads/production/vuforia_target/image/1/iphone_4_887766a119514ece8ed8cd2dbbc16254e3a9ba02.jpg'
},
iphone_5: {
url: 'https://captivereach-crdemo.s3.amazonaws.com/uploads/production/vuforia_target/image/1/iphone_5_887766a119514ece8ed8cd2dbbc16254e3a9ba02.jpg'
},
main: {
url: 'https://captivereach-crdemo.s3.amazonaws.com/uploads/production/vuforia_target/image/1/main_887766a119514ece8ed8cd2dbbc16254e3a9ba02.jpg'
},
galaxy_s3: {
url: 'https://captivereach-crdemo.s3.amazonaws.com/uploads/production/vuforia_target/image/1/galaxy_s3_887766a119514ece8ed8cd2dbbc16254e3a9ba02.jpg'
},
iphone_6: {
url: 'https://captivereach-crdemo.s3.amazonaws.com/uploads/production/vuforia_target/image/1/iphone_6_887766a119514ece8ed8cd2dbbc16254e3a9ba02.jpg'
},
ipad_non_retina: {
url: 'https://captivereach-crdemo.s3.amazonaws.com/uploads/production/vuforia_target/image/1/ipad_non_retina_887766a119514ece8ed8cd2dbbc16254e3a9ba02.jpg'
},
nexus_7_2012: {
url: 'https://captivereach-crdemo.s3.amazonaws.com/uploads/production/vuforia_target/image/1/nexus_7_2012_887766a119514ece8ed8cd2dbbc16254e3a9ba02.jpg'
},
fullscreen_hd: {
url: 'https://captivereach-crdemo.s3.amazonaws.com/uploads/production/vuforia_target/image/1/fullscreen_hd_887766a119514ece8ed8cd2dbbc16254e3a9ba02.jpg'
},
main_hd: {
url: 'https://captivereach-crdemo.s3.amazonaws.com/uploads/production/vuforia_target/image/1/main_hd_887766a119514ece8ed8cd2dbbc16254e3a9ba02.jpg'
},
iphone_6_plus: {
url: 'https://captivereach-crdemo.s3.amazonaws.com/uploads/production/vuforia_target/image/1/iphone_6_plus_887766a119514ece8ed8cd2dbbc16254e3a9ba02.jpg'
},
galaxy_s4: {
url: 'https://captivereach-crdemo.s3.amazonaws.com/uploads/production/vuforia_target/image/1/galaxy_s4_887766a119514ece8ed8cd2dbbc16254e3a9ba02.jpg'
},
nexus_7_2013: {
url: 'https://captivereach-crdemo.s3.amazonaws.com/uploads/production/vuforia_target/image/1/nexus_7_2013_887766a119514ece8ed8cd2dbbc16254e3a9ba02.jpg'
},
ipad_retina: {
url: 'https://captivereach-crdemo.s3.amazonaws.com/uploads/production/vuforia_target/image/1/ipad_retina_887766a119514ece8ed8cd2dbbc16254e3a9ba02.jpg'
},
nexus_10_2013: {
url: 'https://captivereach-crdemo.s3.amazonaws.com/uploads/production/vuforia_target/image/1/nexus_10_2013_887766a119514ece8ed8cd2dbbc16254e3a9ba02.jpg'
},
thumb: {
url: 'https://captivereach-crdemo.s3.amazonaws.com/uploads/production/vuforia_target/image/1/thumb_887766a119514ece8ed8cd2dbbc16254e3a9ba02.jpg'
}
},
mp4_video: 'link',
callbacks: []
};
t.context.insertResponse = {
data: {
id: '107',
type: 'dynamic_content__contents',
attributes: {
title: 'new title',
description: null,
position: 1,
created_at: '2017-07-26T23:06:06Z',
updated_at: '2017-07-26T23:06:06Z',
heart_rate: 100,
likes: 0
},
relationships: {
content_type: {
data: {
id: 76,
type: 'dynamic_content__content_types'
}
}
}
}
};
t.context.updateResponse = {
data: {
type: 'dynamic_content__contents',
id: 1,
attributes: {
title: 'new title',
heart_rate: 100,
visibility_status: 'hidden'
}
},
format: 'json'
};
});
test.after('server shut down', () => {});
test.beforeEach((t) => {
client.authToken = 'valid_token';
client.cache = 'never';
nock(client.apiBaseUrl).post(`/api/dynamic/custom_dates/filter`).reply(200, {
content: [{
id: 8524,
title: 'Dummy 1 Date March 12',
date: '03/12/2016',
content_type: 'custom_dates'
}]
});
t.context.storage = client.storageUtility;
t.context.contentJsonApi = {
data: {
id: 755256,
type: 'dynamic_content__contents',
attributes: {
created_at: '2016-05-12T22:06:38Z',
description: 'Workplace as a Service',
position: 2,
title: 'WPaaS',
tags: [],
content_type: 'acronyms'
},
relationships: {
content_type: {
data: {
id: '183',
type: 'dynamic_content__content_types'
}
}
}
}
};
t.context.content = new Content(t.context.contentJsonApi);
});
// ContentService.delete method
/**
* @test {ContentService.delete}
*/
test('[unit] ContentService.delete should throw errors for invalid parameters', async (t) => {
const title = `ContentService.delete error`;
await t.throwsAsync(contentService.delete(), `${title}: content is not defined`);
await t.throwsAsync(contentService.delete({}), `${title}: content does not have id and content does not have type`);
await t.throwsAsync(contentService.delete({ id: 5 }), `${title}: content does not have type`);
await t.throwsAsync(contentService.delete({ type: 'trials' }), `${title}: content does not have id`);
});
/**
* @test {ContentService.delete}
*/
test('[unit] ContentService.delete should make a properly formatted delete request and response', async (t) => {
const { content } = t.context;
let request = {};
nock(client.apiBaseUrl).delete(`/v3/dynamic_content/contents/${content.id}`).reply(function () {
request = this.req;
return [200, ''];
});
// const response = await clinical6.delete(content);
const response = await contentService.delete(content);
t.is(request.path, `/v3/dynamic_content/contents/${content.id}`);
t.is(request.headers.accept, 'application/json');
t.is(request.headers['content-type'], 'application/json');
t.is(request.headers.authorization, 'Token token=valid_token');
t.falsy(response);
});
/**
* @test {ContentService.delete}
*/
test('[unit] ContentService.delete should remove the element from local storage', async (t) => {
const { content, storage } = t.context;
await storage.set('dynamic_content__contents', content.toJSON(), { id: content.id });
await contentService.delete(content);
t.is(storage.has('dynamic_content__contents', { id: content.id }), false);
});
// ContentService.getTypes method
/**
* @test {ContentService.getTypes}
*/
test.serial('[unit] ContentService.getTypes should throw an error when there is no authToken', (t) => {
client.authToken = undefined;
const expectedError = 'ContentService.getTypes error: requires authToken';
t.throws(() => contentService.getTypes(), expectedError);
});
/**
* @test {ContentService.getTypes}
*/
test('[unit] ContentService.getTypes should make a properly formatted GET request and response', async (t) => {
const { getResponseTypesAll } = t.context;
let request = {};
nock(client.apiBaseUrl).get(`/v3/dynamic_content/content_types`).reply(function (uri, requestBody) {
request = this.req;
request.requestBody = requestBody;
return [200, getResponseTypesAll];
});
const response = await contentService.getTypes();
t.is(request.path, `/v3/dynamic_content/content_types`);
t.is(request.headers.accept, 'application/json');
t.is(request.headers['content-type'], 'application/json');
t.truthy(response);
t.is(response.length, 3);
t.is(response[0].id, 76);
t.is(response[0].type, 'dynamic_content__content_types');
t.is(response[0].name, 'dummy_407');
t.is(response[0].permanentLink, 'home_health_visit');
t.is(response[0].description, null);
t.is(response[0].createdAt, '2017-07-28T15:30:39Z');
t.is(response[0].updatedAt, '2017-07-28T15:30:39Z');
t.is(response[1].id, 182);
t.is(response[1].type, 'dynamic_content__content_types');
t.is(response[1].name, 'Mayor and Council');
t.is(response[1].permanentLink, 'mayor_and_council');
t.is(response[1].description, null);
t.is(response[1].createdAt, '2017-07-28T15:30:40Z');
t.is(response[1].updatedAt, '2017-07-28T15:30:40Z');
t.is(response[2].id, 183);
t.is(response[2].type, 'dynamic_content__content_types');
t.is(response[2].name, 'Mayor and Council');
t.is(response[2].permanentLink, 'acronyms');
t.is(response[2].description, null);
t.is(response[2].createdAt, '2017-07-28T15:30:40Z');
t.is(response[2].updatedAt, '2017-07-28T15:30:40Z');
});
/**
* @test {ContentService.getTypes(id)}
*/
test('[unit] ContentService.getTypes(id) should receive a valid response for a get request', async (t) => {
const { getResponseTypesId } = t.context;
nock(client.apiBaseUrl).get(`/v3/dynamic_content/content_types`).reply(200, getResponseTypesId);
const id = { id: 76 };
const response = await contentService.getTypes(id);
t.truthy(response);
t.is(response.id, 76);
t.is(response.type, 'dynamic_content__content_types');
t.is(response.name, 'dummy_407');
t.is(response.permanentLink, 'home_health_visit');
t.is(response.description, null);
t.is(response.createdAt, '2017-07-28T15:30:39Z');
t.is(response.updatedAt, '2017-07-28T15:30:39Z');
});
// ContentService.getContent method
/**
* @test {ContentService.getContent}
*/
test('[unit] ContentService.getContent should throw an error when resource is not defined', async (t) => {
await t.throwsAsync(contentService.getContent(), 'ContentService.getContent error: resource is not defined');
await t.throwsAsync(contentService.getContent(23), 'ContentService.getContent error: resource is not a string');
});
/**
* @test {ContentService.getContent}
*/
test('[unit] ContentService.getContent should make a properly formatted GET request', async (t) => {
let request = {};
nock(client.apiBaseUrl).get(`/v3/dynamic_content/contents?filters[content_type][permanent_link]=permanent_link`).reply(function (uri, requestBody) {
request = this.req;
request.requestBody = requestBody;
return [200, {}];
});
await contentService.getContent('permanent_link');
t.is(request.path, `/v3/dynamic_content/contents?filters[content_type][permanent_link]=permanent_link`);
t.is(request.headers.accept, 'application/json');
t.is(request.headers['content-type'], 'application/json');
});
/**
* @test {ContentService.getContent}
*/
test('[unit] ContentService.getContent should receive a valid response for a get request (basic_flow)', async (t) => {
nock(client.apiBaseUrl).get(`/v3/dynamic_content/contents?filters[content_type][permanent_link]=basic_flow`).reply(200, {
data: [
{
id: '1',
type: 'dynamic_content__contents',
attributes: {
progress_bar: null, help_text: null, title: 'First', description: null, image_url: '/assets/default/default-16daaa70b849a969f5ab41d3b7c9513c8ca890a6dd3b30e39c5afc25b8a773b6.png', position: 1, status: 'enabled', created_at: '2018-02-02T19:56:31Z', updated_at: '2018-02-02T19:56:31Z'
},
relationships: { content_type: { data: { id: '1', type: 'dynamic_content__content_types' } } }
},
{
id: '2',
type: 'dynamic_content__contents',
attributes: {
progress_bar: null, help_text: null, title: 'Second Screen', description: null, image_url: '/assets/default/default-16daaa70b849a969f5ab41d3b7c9513c8ca890a6dd3b30e39c5afc25b8a773b6.png', position: 2, status: 'enabled', created_at: '2018-02-02T19:59:52Z', updated_at: '2018-02-02T19:59:52Z'
},
relationships: { content_type: { data: { id: '1', type: 'dynamic_content__content_types' } } }
},
{
id: '3',
type: 'dynamic_content__contents',
attributes: {
progress_bar: false, help_text: 'Yes it is', title: 'Question', description: '\u003cdiv\u003eIs this not a test?\u003c/div\u003e', image_url: '/assets/default/default-16daaa70b849a969f5ab41d3b7c9513c8ca890a6dd3b30e39c5afc25b8a773b6.png', position: 3, status: 'enabled', created_at: '2018-02-20T19:24:27Z', updated_at: '2018-02-20T19:27:05Z'
},
relationships: { content_type: { data: { id: '1', type: 'dynamic_content__content_types' } } }
}
]
});
const response = await contentService.getContent('basic_flow');
t.truthy(response);
t.is(response.length, 3);
t.is(response[0].id, 1);
t.is(response[0].title, 'First');
t.is(response[1].id, 2);
t.is(response[1].title, 'Second Screen');
t.is(response[2].id, 3);
t.is(response[2].title, 'Question');
});
// ContentService.getContentFavorites method
/**
* @test {ContentService.getContentFavorites}
*/
test('[unit] ContentService.getContentFavorites should throw an error when resource is not defined', async (t) => {
await t.throwsAsync(contentService.getContentFavorites(), 'ContentService.getContentFavorites error: resource is not defined');
await t.throwsAsync(contentService.getContentFavorites(23), 'ContentService.getContentFavorites error: resource is not a string');
});
/**
* @test {ContentService.getContentFavorites}
*/
test('[unit] ContentService.getContentFavorites should make a properly formatted GET request and response', async (t) => {
const { getResponseFavorites } = t.context;
let request = {};
nock(client.apiBaseUrl).get(`/api/dynamic/permanent_link/favorites?page=1&per_page=10`).reply(function (uri, requestBody) {
request = this.req;
request.requestBody = requestBody;
return [200, getResponseFavorites];
});
const response = await contentService.getContentFavorites('permanent_link', 1, 10);
t.is(request.path, `/api/dynamic/permanent_link/favorites?page=1&per_page=10`);
t.is(request.headers.accept, 'application/json');
t.is(request.headers['content-type'], 'application/json');
t.truthy(response);
t.is(response.length, 3);
t.is(response[0].id, 1);
t.is(response[0].title, 'R. Scott Silverthorne');
t.is(response[0].titleLegend, 'Serving his second term as Mayor.');
t.is(response[0].rank, 'Mayor');
t.is(response[0].contentType.permanentLink, 'mayor_and_council');
t.is(response[0].email, 'Scott.silverthorne@fairfaxva.gov');
t.is(response[0].website, 'http://www.fairfaxva.gov/government/mayor-city-council/mayor-r-scott-silverthorne');
t.is(response[1].id, 2);
t.is(response[1].title, 'Michael J. DeMarco');
t.is(response[1].titleLegend, 'Serving his second term on City Council.');
t.is(response[1].rank, 'Councilmember');
t.is(response[1].contentType.permanentLink, 'mayor_and_council');
t.is(response[1].email, 'Michael.DeMarco@fairfaxva.gov');
t.is(response[1].website, 'http://www.fairfaxva.gov/government/mayor-city-council/councilmember-michael-j-demarco');
t.is(response[2].id, 3);
t.is(response[2].title, 'Jeffrey C. Greenfield');
t.is(response[2].titleLegend, 'Serving his eleventh term on City Council.');
t.is(response[2].rank, 'Councilmember');
t.is(response[2].contentType.permanentLink, 'mayor_and_council');
t.is(response[2].email, 'Jeff.greenfield@fairfaxva.gov');
t.is(response[2].website, 'http://www.fairfaxva.gov/government/mayor-city-council/councilmember-jeffrey-c-greenfield');
t.is(response[2].contentType.permanentLink, 'mayor_and_council');
});
// ContentService.getById method
/**
* @test {ContentService.getById}
*/
test('[unit] ContentService.getById should throw an error when id is not defined', async (t) => {
const title = `ContentService.getById error`;
await t.throwsAsync(contentService.getById(), `${title}: id is not defined`);
});
/**
* @test {ContentService.getById}
*/
test('[unit] ContentService.getById should make a properly formatted GET request and response', async (t) => {
const { getResponseId } = t.context;
let request = {};
nock(client.apiBaseUrl).get(`/v3/dynamic_content/contents/655256`).reply(function (uri, requestBody) {
request = this.req;
request.requestBody = requestBody;
return [200, getResponseId];
});
const response = await contentService.getById(655256);
t.is(request.path, `/v3/dynamic_content/contents/655256`);
t.is(request.headers.accept, 'application/json');
t.is(request.headers['content-type'], 'application/json');
t.truthy(response);
t.is(response.description, 'Workplace as a Service');
t.is(response.id, 655256);
t.is(response.position, 2);
t.is(response.title, 'WPaaS');
t.is(response.contentType.permanentLink, 'acronyms');
});
// ContentService.getRandom method
/**
* @test {ContentService.getRandom}
*/
test('[unit] ContentService.getRandom should throw an error when id is not defined', async (t) => {
await t.throwsAsync(contentService.getRandom(), 'ContentService.getRandom error: resource is not defined');
await t.throwsAsync(contentService.getRandom(53), 'ContentService.getRandom error: resource is not a string');
});
/**
* @test {ContentService.getRandom}
*/
test('[unit] ContentService.getRandom should make a properly formatted GET request and response', async (t) => {
const { getResponseRandom } = t.context;
let request = {};
nock(client.apiBaseUrl).get(`/api/dynamic/permanent_link/random`).reply(function (uri, requestBody) {
request = this.req;
request.requestBody = requestBody;
return [200, getResponseRandom];
});
const response = await contentService.getRandom('permanent_link');
t.is(request.path, `/api/dynamic/permanent_link/random`);
t.is(request.headers.accept, 'application/json');
t.is(request.headers['content-type'], 'application/json');
t.truthy(response);
t.is(response.description, 'Workplace as a Service');
t.is(response.id, 655256);
t.is(response.position, 2);
t.is(response.title, 'WPaaS');
t.is(response.contentType.permanentLink, 'permanent_link');
});
// ContentService.insert method
/**
* @test {ContentService.insert}
*/
test('[unit] ContentService.insert should throw errors for a missing content parameter and attributes', async (t) => {
const pre = `ContentService.insert error`;
await t.throwsAsync(contentService.insert(), `${pre}: content is not defined and content is not a type of Content`);
const content = { title: 'new title' };
await t.throwsAsync(contentService.insert(content), `${pre}: content is not a type of Content`);
});
/**
* @test {ContentService.insert}
*/
test('[unit] ContentService.insert should successfully insert content with a content object', async (t) => {
const { insertResponse } = t.context;
let request = {};
nock(client.apiBaseUrl).post(`/v3/dynamic_content/contents`).reply(function (uri, requestBody) {
request = this.req;
request.requestBody = requestBody;
return [200, insertResponse];
});
const content = new Content('home_health_visit', {
title: 'new title'
});
const response = await contentService.insert(content);
t.is(request.path, `/v3/dynamic_content/contents`);
t.is(request.headers.accept, 'application/json');
t.deepEqual(request.requestBody, {
data: {
type: 'dynamic_content__contents',
attributes: { title: 'new title' },
relationships: {
content_type: {
data: {
id: 76,
type: 'dynamic_content__content_types'
}
}
}
}
});
t.is(request.headers['content-type'], 'application/json');
t.is(request.headers.authorization, 'Token token=valid_token');
t.is(response.title, 'new title');
t.is(response.contentType.permanentLink, 'home_health_visit');
t.is(response.id, 107);
// Make sure content.id is now 107
t.is(content.id, 107);
});
// ContentService.update method
/**
* @test {ContentService.update}
*/
test('[unit] ContentService.update should throw errors for a missing content parameter', async (t) => {
const undefinedError = 'ContentService.update error: content is not defined'
+ ' and content is not a type of Content';
// undefined content
await t.throwsAsync(contentService.update(), undefinedError);
});
/**
* @test {ContentService.update}
*/
test('[unit] ContentService.update should throw errors for a missing content id', async (t) => {
const undefinedError = 'ContentService.update error: '
+ 'content does not have id'
+ ' and content is not a type of Content';
const content = {
title: 'new title',
content_type: 'permanent_link',
};
// undefined content
await t.throwsAsync(contentService.update(content), undefinedError);
});
/**
* @test {ContentService.update}
*/
test('[unit] ContentService.update should throw errors for a missing content type', async (t) => {
const undefinedError = 'ContentService.update error: '
+ 'content is not a type of Content';
const content = {
id: 1,
title: 'new title',
};
await t.throwsAsync(contentService.update(content), undefinedError);
});
/**
* @test {ContentService.update}
*/
test.serial('[unit] ContentService.update should successfully update content with a content object', async (t) => {
const { updateResponse, getResponseTypesAll } = t.context;
let request = {};
nock(client.apiBaseUrl).get(`/v3/dynamic_content/content_types`).reply([200, getResponseTypesAll]);
nock(client.apiBaseUrl).patch(`/v3/dynamic_content/contents/1`).reply(function (uri, requestBody) {
request = this.req;
request.requestBody = requestBody;
return [200, updateResponse];
});
const content = new Content('acronyms', {
id: 1,
title: 'new title',
});
const response = await contentService.update(content);
t.is(request.path, `/v3/dynamic_content/contents/1`);
t.is(request.headers.accept, 'application/json');
t.deepEqual(request.requestBody, {
data: {
id: 1,
type: 'dynamic_content__contents',
attributes: { title: 'new title' },
relationships: {
content_type: {
data: {
id: 183,
type: 'dynamic_content__content_types'
}
}
}
}
});
t.is(request.headers['content-type'], 'application/json');
t.is(request.headers.authorization, 'Token token=valid_token');
t.is(response.title, 'new title');
t.is(response.heartRate, 100);
t.is(response.visibilityStatus, 'hidden');
// t.is(response.contentType.permanentLink, 'acronyms');
});
// ContentService.getVuforiaDynamicContent method
/**
* @test {ContentService.getVuforiaDynamicContent}
*/
test.serial('[unit] ContentService.getVuforiaDynamicContent should throw an error when there is no authToken or id parameter', async (t) => {
client.authToken = undefined;
const title = `ContentService.getVuforiaDynamicContent error`;
await t.throwsAsync(contentService.getVuforiaDynamicContent(), `${title}: requires authToken and id is not defined`);
client.authToken = 'valid_token';
await t.throwsAsync(contentService.getVuforiaDynamicContent(), `${title}: id is not defined`);
});
/**
* @test {ContentService.getVuforiaDynamicContent}
*/
test('[unit] ContentService.getVuforiaDynamicContent should make a properly formatted get request and response', async (t) => {
const { getResponseVuforia } = t.context;
let request = {};
nock(client.apiBaseUrl).get(`/api/dynamic/1/vuforia_target`).reply(function (uri, requestBody) {
request = this.req;
request.requestBody = requestBody;
return [200, getResponseVuforia];
});
const response = await contentService.getVuforiaDynamicContent(1);
t.is(request.path, `/api/dynamic/1/vuforia_target`);
t.is(request.headers.accept, 'application/json');
t.is(request.headers['content-type'], 'application/json');
t.is(request.headers.authorization, 'Token token=valid_token');
t.truthy(response);
t.is(response.vuforia_id, '3769864600964fd4909323a64235e1be');
t.is(response.name, 'Test');
t.is(response.mp4_video, 'link');
t.is(response.callbacks.length, 0);
});
// StatusService.getStatus method
/**
* @test {StatusService.getStatus}
*/
test.serial('[unit] StatusService.getStatus should throw an error if invalid requirements', async (t) => {
client.authToken = undefined;
const title = `StatusService.getStatus error`;
await t.throwsAsync(statusService.getStatus(),
`${title}: requires authToken`
+ ' and type is not defined'
+ ' and object is not defined'
+ ' and ownerType is not defined'
+ ' and owner is not defined');
client.authToken = 'valid_token';
await t.throwsAsync(statusService.getStatus(),
`${title}: type is not defined`
+ ' and object is not defined'
+ ' and ownerType is not defined'
+ ' and owner is not defined');
await t.throwsAsync(statusService.getStatus('type'),
`${title}: object is not defined`
+ ' and ownerType is not defined'
+ ' and owner is not defined');
await t.throwsAsync(statusService.getStatus('type', 'object'),
`${title}: ownerType is not defined`
+ ` and owner is not defined`);
await t.throwsAsync(statusService.getStatus('type', 'object', 'site_id'),
`${title}: owner is not defined`);
});
/**
* @test {StatusService.getStatus}
*/
test('[unit] StatusService.getStatus should successfully return all status values', async (t) => {
const { getResponseStatus } = t.context;
let request = {};
nock(client.apiBaseUrl).post(`/api/status/check`).reply(function (uri, requestBody) {
request = this.req;
request.requestBody = requestBody;
return [200, getResponseStatus];
});
const response = await statusService.getStatus('type', 'object', 'site_id', 'MobileUser');
t.is(request.path, `/api/status/check`);
t.is(request.headers.accept, 'application/json');
t.is(request.headers.authorization, 'Token token=valid_token');
t.deepEqual(request.requestBody, {
status: {
object_type: 'type',
object: 'object',
owner_type: 'site_id',
owner: 'MobileUser',
},
});
t.is(request.headers['content-type'], 'application/json');
t.is(response.status.object, 'object');
t.is(response.status.type, 'type');
t.is(response.status.value, '');
});
// ContentService.filter method
// ContentService.filter method simple
/**
* @test {ContentService.filter}
*/
test.serial('[unit] ContentService.filter should throw an error when resource is not specified correctly', async (t) => {
client.authToken = undefined;
const expectedError = 'ContentService.filter error: requires authToken'
+ ' and query is not defined'
+ ' and query is not a type of Query';
await await t.throwsAsync(contentService.filter(), expectedError);
});
/**
* @test {ContentService.filter}
*/
test('[unit] ContentService.filter should throw an error when query is not of type Query', async (t) => {
const expectedError = 'ContentService.filter error: query is not a type of Query'
+ ' and query does not have _resource';
await await t.throwsAsync(contentService.filter('someparmalink'), expectedError);
});
/**
* @test {ContentService.filter}
*/
test('[unit] ContentService.filter should throw an error when there are no search values to query', async (t) => {
const expectedError = 'Query.validate mode search error: please add search values to Query';
await await t.throwsAsync(contentService.filter(new Query('someparmalink')), expectedError);
});
/**
* @test {ContentService.filter}
*/
test.serial('[unit] ContentService.filter should throw an error if client authToken is not set', async (t) => {
client.authToken = undefined;
const query = new Query('resource');
query.addSearchValue('silverthorne');
await await t.throwsAsync(contentService.filter(query), 'ContentService.filter error: requires authToken');
});
/**
* @test {ContentService.filter}
*/
test('[unit] ContentService.filter should make a properly formatted POST request', async (t) => {
let request = {};
nock(client.apiBaseUrl).post(`/api/dynamic/resource/filter`).reply(function (uri, requestBody) {
request = this.req;
request.requestBody = requestBody;
return [200, { status: 'ok', content: [], total_pages: 1 }];
});
const query = new Query('resource');
query.addSearchValue('Aesthetic');
query.addSearchValue('Creamery');
await contentService.filter(query);
// get the most recent request
// validate created request
t.is(request.path, `/api/dynamic/resource/filter`);
t.is(request.headers.accept, 'application/json');
t.is(request.headers['content-type'], 'application/json');
t.is(request.headers.authorization, 'Token token=valid_token');
t.deepEqual(request.requestBody, {
filters: [{
attribute: 'search',
values: ['Aesthetic', 'Creamery']
}],
order_by: [],
minimal: false,
brand_relative: false,
reference_module: '',
page: 0,
per_page: 0,
});
});
/**
* @test {ContentService.filter}
*/
test('[unit] ContentService.filter should get a valid response for a POST request with search', async (t) => {
const { filterResponse } = t.context;
let request = {};
nock(client.apiBaseUrl).post(`/api/dynamic/mayor_and_council/filter`).reply(function (uri, requestBody) {
request = this.req;
request.requestBody = requestBody;
return [200, filterResponse];
});
const query = new Query('mayor_and_council');
query.addSearchValue('silverthorne');
const response = await contentService.filter(query);
t.is(request.path, `/api/dynamic/mayor_and_council/filter`);
t.is(request.headers.accept, 'application/json');
t.is(request.headers['content-type'], 'application/json');
t.is(request.headers.authorization, 'Token token=valid_token');
t.deepEqual(request.requestBody, {
filters: [{
attribute: 'search',
values: ['silverthorne']
}],
order_by: [],
minimal: false,
brand_relative: false,
reference_module: '',
page: 0,
per_page: 0,
});
t.is(response[0].brand.id, 1);
t.is(response[0].contentType.permanentLink, 'mayor_and_council');
t.is(response[0].description, 'Mayoral Description blah blah blah');
t.is(response[0].email, 'Scott.silverthorne@fairfaxva.gov');
t.is(response[0].email2, 'cityclerk@fairfaxva.gov');
t.is(response[0].id, 1);
t.is(response[0].image.url, 'https://captivereach-dhsadobe.s3.amazonaws.com/1/mayor_1.jpg');
t.is(response[0].phone, '703-385-7800');
t.is(response[0].position, 1);
t.is(response[0].rank, 'Mayor');
t.is(response[0].title, 'R. Scott Silverthorne');
t.is(response[0].titleLegend, 'Serving his second term as Mayor.');
t.is(response[0].website, 'http://www.fairfaxva.gov/government/mayor/r-scott-silverthorne');
});
// ContentService.getBySearch method
// /**
// * @test {ContentService.filter}
// */
// test('[unit] ContentService.getBySearch should make a properly formatted POST request', async (t) => {
// const query = new Query('resource');
// query.addSearchValue('silverthorne');
// await contentService.filter(query);
// // get the most recent request
// // validate created request
// t.is(request.path, `/api/dynamic/resource/filter`);
// t.is(request.headers.accept, 'application/json');
// t.is(request.headers['content-type'], 'application/json');
// t.is(request.headers.authorization, 'Token token=valid_token');
// t.deepEqual(request.requestBody, {
// filters: [{
// attribute: 'search',
// values: ['silverthorne']
// }],
// order_by: [],
// minimal: false,
// brand_relative: false,
// reference_module: '',
// page: 0,
// per_page: 0,
// }));
// });
// ContentService.getByNumber method
/**
* @test {ContentService.filter}
*/
test('[unit] ContentService.getByNumber should throw an error when getByNumber is called without max,min,or values', async (t) => {
const query = new Query('someparmalink');
query.searchByNumberMode();
const expectedError = 'Query.validate mode numeric error: numeric min, max, or values not specified';
await t.throwsAsync(contentService.filter(query), expectedError);
});
/**
* @test {ContentService.filter}
*/
test('[unit] ContentService.getByNumber should make a properly formatted POST request and response', async (t) => {
let request = {};
nock(client.apiBaseUrl).post(`/api/dynamic/custom_contents/filter`).reply(function (uri, requestBody) {
request = this.req;
request.requestBody = requestBody;
return [200, {
content: [{
id: 8526,
title: 'Dummy 3 Number 5',
number: '5',
content_type: 'custom_contents'
}]
}];
});
const query = new Query('someparmalink');
query.searchByNumberMode();
query.setResource('custom_contents')
.lessThanOrEqualTo(100)
.greaterThanOrEqualTo(5)
.setValue(5);
const response = await contentService.filter(query);
// get the most recent request
// validate created request
t.is(request.path, `/api/dynamic/custom_contents/filter`);
t.is(request.headers.accept, 'application/json');
t.is(request.headers['content-type'], 'application/json');
t.is(request.headers.authorization, 'Token token=valid_token');
t.deepEqual(request.requestBody, {
filters: [{
attribute: 'numeric',
max: 100,
min: 5,
value: 5
}],
order_by: [],
minimal: false,
brand_relative: false,
reference_module: '',
page: 0,
per_page: 0,
});
t.is(response.length, 1);
t.is(response[0].id, 8526);
t.is(response[0].title, 'Dummy 3 Number 5');
t.is(parseInt(response[0].number, 10), 5);
t.is(response[0].contentType.permanentLink, 'custom_contents');
});
// ContentService.getByLocation method
/**
* @test {ContentService.filter}
*/
test('[unit] ContentService.getByLocation should throw an error when getByLocation is called incorrectly', async (t) => {
let query = new Query('someparmalink');
query.searchByLocationMode();
const expectedErrorRadius = 'location radius exact lng and exact lat not specified';
const expectedErrorValue = 'location address value not specified';
const expectedErrorBox = 'location not all box coordinates specified';
// radius location validation
await t.throwsAsync(() => {
query.setRadius('10');
return contentService.filter(query);
}, expectedErrorRadius);
// box location validation
await t.throwsAsync(() => {
query = new Query('someparmalink');
query.searchByLocationMode();
query.setSwLat('10.0');
query.setSwLong('9.9');
query.setNeLat('9.9');
return contentService.filter(query);
}, expectedErrorBox);
// address location validation
await t.throwsAsync(() => {
query = new Query('someparmalink');
query.searchByLocationMode();
return contentService.filter(query);
}, expectedErrorValue);
});
/**
* @test {ContentService.filter}
*/
test('[unit] ContentService.getByLocation should make a properly formatted POST request', async (t) => {
let request = {};
nock(client.apiBaseUrl).post(`/api/dynamic/custom_addresses/filter`).reply(function (uri, requestBody) {
request = this.req;
request.requestBody = requestBody;
return [200, {
content: [{
id: 1165,
title: 'Ashby Pond Conservatory',
address: '9817 Ashby RoadFairfax VA 22031',
content_type: 'park'
}, {
id: 1166,
title: 'Cobbdale Park',
address: '3600 Burrows Avenue Fairfax, VA 22030',
content_type: 'park'
}]
}];
});
const query = new Query('custom_addresses', [], 1, 10, false);
query.searchByAddressMode();
query.setRadius('1');
query.setExactLatitude('38.8806313');
query.setExactLongitude('-77.2275811');
await contentService.filter(query);
// get the most recent request
// validate created request
t.is(request.path, `/api/dynamic/custom_addresses/filter`);
t.is(request.headers.accept, 'application/json');
t.is(request.headers['content-type'], 'application/json');
t.is(request.headers.authorization, 'Token token=valid_token');
t.deepEqual(request.requestBody, {
filters: [{
attribute: 'address',
radius: '1',
exact_lat: '38.8806313',
exact_lng: '-77.2275811'
}],
order_by: [],
minimal: false,
brand_relative: false,
reference_module: '',
page: 1,
per_page: 10,
});
});
/**
* @test {ContentService.filter}
*/
test('[unit] ContentService.getByLocation should get a valid response for a POST request for location radius', async (t) => {
nock(client.apiBaseUrl).post(`/api/dynamic/custom_addresses/filter`).reply(200, {
content: [{
id: 1165,
title: 'Ashby Pond Conservatory',
address: '9817 Ashby RoadFairfax VA 22031',
content_type: 'park'
}, {
id: 1166,
title: 'Cobbdale Park',
address: '3600 Burrows Avenue Fairfax, VA 22030',
content_type: 'park'
}]
});
const query = new Query('custom_addresses', [], 1, 999, true);
query.searchByAddressMode();
query.setRadius('1');
query.setExactLatitude('38.8806313');
query.setExactLongitude('-77.2275811');
const response = await contentService.filter(query);
t.truthy(response);
t.is(response.length, 2);
t.is(response[0].id, 1165);
t.is(response[0].title, 'Ashby Pond Conservatory');
t.is(response[0].address, '9817 Ashby RoadFairfax VA 22031');
t.is(response[0].contentType.permanentLink, 'park');
t.is(response[1].id, 1166);
t.is(response[1].title, 'Cobbdale Park');
t.is(response[1].contentType.permanentLink, 'park');
t.is(response[1].address, '3600 Burrows Avenue Fairfax, VA 22030');
});
/**
* @test {ContentService.filter}
*/
test('[unit] ContentService.getByLocation should get a valid response for a POST request for address match', async (t) => {
nock(client.apiBaseUrl).post(`/api/dynamic/custom_addresses/filter`).reply(200, {
content: [{
id: 1165,
title: 'Ashby Pond Conservatory',
address: '9817 Ashby RoadFairfax VA 22031',
content_type: 'park'
}]
});
const query = new Query('custom_addresses', [], 1, 999, true);
query.searchByAddressMode();
query.addSearchValue('Ashby');
const response = await contentService.filter(query);
t.truthy(response);
t.is(response.length, 1);
t.is(response[0].id, 1165);
t.is(response[0].title, 'Ashby Pond Conservatory');
t.is(response[0].address, '9817 Ashby RoadFairfax VA 22031');
t.is(response[0].contentType.permanentLink, 'park');
});
/**
* @test {ContentService.filter}
*/
test('[unit] ContentService.getByLocation should get a valid response for a POST request for box filter', async (t) => {
nock(client.apiBaseUrl).post(`/api/dynamic/custom_addresses/filter`).reply(200, {
content: [{
id: 5350,
title: 'Gateway Regional Park',
address: '3333 Old Pickett Road Fairfax, VA 22031',
content_type: 'park'
}, {
id: 5351,
title: 'Green Acres Center',
address: '',
content_type: 'park'
}, {
id: 5352,
title: 'Old Town Square',
address: '10415 North Street Fairfax, VA 22030',
content_type: 'park'
}]
});
const query = new Query('custom_addresses');
query.addSearchValue('Ashby');
query.searchByAddressMode();
const response = await contentService.filter(query);
t.truthy(response);
t.is(response.length, 3);
t.is(response[0].id, 5350);
t.is(response[0].title, 'Gateway Regional Park');
t.is(response[0].address, '3333 Old Pickett Road Fairfax, VA 22031');
t.is(response[0].contentType.permanentLink, 'park');
t.is(response[1].id, 5351);
t.is(response[1].title, 'Green Acres Center');
t.is(response[1].address, '');
t.is(response[1].contentType.permanentLink, 'park');
t.is(response[2].id, 5352);
t.is(response[2].title, 'Old Town Square');
t.is(response[2].address, '10415 North Street Fairfax, VA 22030');
t.is(response[2].contentType.permanentLink, 'park');
});
// ContentService.getByTagNames method
/**
* @test {ContentService.filter}
*/
test('[unit] ContentService.getByTagNames should throw an error when getByTagNames is called incorrectly', async (t) => {
const query = new Query('someparmalink');
query.searchByTagNamesMode();
await t.throwsAsync(contentService.filter(query), `Query.validate mode tag_names error: please add search values to Query`);
});
// /**
// * @test {ContentService.filter}
// */
// test('[unit] ContentService.getByTagNames should make a properly formatted POST request', async (t) => {
// const query = new Query('custom_bands');
// query.searchByTagNamesMode();
// query.addSearchValue('Children’s Stage');
// await contentService.filter(query);
// // get the most recent request
// // validate created request
// t.is(request.path, `/api/dynamic/custom_bands/filter`);
// t.is(request.headers.accept, 'application/json');
// t.is(request.headers['content-type'], 'application/json');
// t.is(request.headers.authorization, 'Token token=valid_token');
// t.deepEqual(request.requestBody, {
// filters: [{
// attribute: 'tag_names',
// values: ['Children’s Stage']
// }],
// order_by: [],
// minimal: false,
// brand_relative: false,
// reference_module: '',
// page: 0,
// per_page: 0,
// }));
// });
/**
* @test {ContentService.filter}
*/
test('[unit] ContentService.getByTagNames should get a valid response for a POST request', async (t) => {
const { filterResponseCustomBands } = t.context;
nock(client.apiBaseUrl).post(`/api/dynamic/custom_bands/filter`).reply(200, filterResponseCustomBands);
const query = new Query('custom_bands', [], 1, 10, true);
query.searchByTagNamesMode();
query.addSearchValue('Children’s Stage');
const response = await contentService.filter(query);
t.truthy(response);
t.is(response.length, 3);
t.is(response[0].id, 65);
t.is(response[0].title, 'School of Rock');
t.truthy(response[0].tags);
t.is(response[0].tags[0].name, 'Children’s Stage');
t.is(response[0].contentType.permanentLink, 'bands');
t.is(response[1].id, 66);
t.is(response[1].title, 'Turley the Magician');
t.truthy(response[1].tags);
t.is(response[1].tags[0].name, 'Children’s Stage');
t.is(response[1].contentType.permanentLink, 'bands');
t.is(response[2].id, 67);
t.is(response[2].title, 'Chuck F. - Juggler');
t.truthy(response[2].tags);
t.is(response[2].tags[0].name, 'Children’s Stage');
t.is(response[2].contentType.permanentLink, 'bands');
});
/**
* @test {Clinical6.get}
*/
test('[unit] Clinical6.get({type: \'dynamic_content__public_contents\' }) should get a valid response for a GET request', async (t) => {
const { getPublicContents } = t.context;
nock(client.apiBaseUrl).get(`/v3/dynamic_content/public_contents`).reply(200, getPublicContents);
const response = await clinical6.get({ type: 'dynamic_content__public_contents' });
t.truthy(response);
t.is(response.length, 3);
t.is(response[0].id, 36);
t.is(response[0].title, 'Terms of Use');
t.is(response[0].contentType.permanentLink, 'terms_of_use');
t.is(response[0].description.indexOf('<h2>'), 0); // has html
t.is(response[1].id, 43);
t.is(response[1].title, 'Privacy Policy ');
t.is(response[1].contentType.permanentLink, 'privacy_policy');
t.is(response[2].id, 35);
t.is(response[2].title, 'Anti-Spam Policy');
t.is(response[2].contentType.permanentLink, 'antispam_policy');
});
// ContentService.getByDate method
/**
* @test {ContentService.filter}
*/
test('[unit] ContentService.getByDate should throw an error when getByDate is called without max and/or min', async (t) => {
const query = new Query('someparmalink');
query.searchByDateMode();
const expectedError = `Query.validate mode date error: filter min or max values not specified`;
await t.throwsAsync(contentService.filter(query), expectedError);
});
// /**
// * @test {ContentService.filter}
// */
// test('[unit] ContentService.getByDate should make a properly formatted POST request', async (t) => {
// const query = new Query('someparmalink');
// query.searchByDateMode()
// .setResource('custom_dates')
// .lessThanOrEqualTo('03/31/2016')
// .greaterThanOrEqualTo('02/01/2016');
// await contentService.filter(query);
// // get the most recent request
// // validate created request
// t.is(request.path, `/api/dynamic/custom_dates/filter`);
// t.is(request.headers.accept, 'application/json');
// t.is(request.headers['content-type'], 'application/json');
// t.is(request.headers.authorization, 'Token token=valid_token');
// t.deepEqual(request.requestBody, {
// filters: [{
// attribute: 'date',
// max: '03/31/2016',
// min: '02/01/2016'
// }],
// order_by: [],
// minimal: false,
// brand_relative: false,
// reference_module: '',
// page: 0,
// per_page: 0,
// }));
// });
/**
* @test {ContentService.filter}
*/
test('[unit] ContentService.getByDate should get a valid response for a POST request', async (t) => {
const query = new Query('someparmalink');
query.searchByDateMode()
.setResource('custom_dates')
.lessThanOrEqualTo('03/31/2016')
.greaterThanOrEqualTo('02/01/2016');
const response = await contentService.filter(query);
t.is(response.length, 1);
t.is(response[0].id, 8524);
t.is(response[0].title, 'Dummy 1 Date March 12');
t.is(response[0].date, '03/12/2016');
t.is(response[0].contentType.permanentLink, 'custom_dates');
});
// ContentService.getByTagIds method
/**
* @test {ContentService.filter}
*/
test('[unit] ContentService.getByTagIds should throw an error when getByTagIds is called incorrectly', async (t) => {
const expectedError = 'ContentService.filter error: '
+ 'query is not a type of Query and query does not have _resource';
const expectedError2 = 'Query.validate mode tag error:'
+ ' please add search values to Query';
await t.throwsAsync(contentService.filter('someparmalink', {}), expectedError);
const query = new Query('someparmalink');
query.searchByTagIDMode();
await t.throwsAsync(contentService.filter(query), expectedError2);
});
// /**
// * @test {ContentService.filter}
// */
// test('[unit] ContentService.getByTagIds should make a properly formatted POST request', async (t) => {
// const query = new Query('custom_bands');
// query.addSearchValue(1);
// query.addSearchValue(2);
// query.searchByTagIDMode();
// // client.authToken = 'valid_token';
// await contentService.filter(query);
// // get the most recent request
// // validate created request
// t.is(request.path, `/api/dynamic/custom_bands/filter`);
// t.is(request.headers.accept, 'application/json');
// t.is(request.headers['content-type'], 'application/json');
// t.is(request.headers.authorization, 'Token token=valid_token');
// t.deepEqual(request.requestBody, {
// filters: [{
// attribute: 'tag',
// values: [1, 2]
// }],
// order_by: [],
// minimal: false,
// brand_relative: false,
// reference_module: '',
// page: 0,
// per_page: 0,
// }));
// });
/**
* @test {ContentService.filter}
*/
test('[unit] ContentService.getByTagIds should get a valid response for a POST request', async (t) => {
nock(client.apiBaseUrl).post(`/api/dynamic/custom_bands/filter`).reply(200, {
content: [{
id: 61,
title: 'Almost Journey/2U',
tags: [{
created_at: '2014-09-13T00:14:45Z',
id: 1,
name: 'Main Stage',
position: 1,
updated_at: '2014-09-13T00:14:45Z'
}],
content_type: 'bands'
}, {
id: 62,
title: 'Bruce in the USA',
tags: [{
created_at: '2014-09-13T00:14:45Z',
id: 1,
name: 'Main Stage',
position: 1,
updated_at: '2014-09-13T00:14:45Z'
}],
content_type: 'bands'
}, {
id: 63,
title: 'Kevin MaC Band',
tags: [{
created_at: '2014-09-13T00:14:51Z',
id: 2,
name: 'Country Stage',
position: 2,
updated_at: '2014-09-13T00:14:51Z'
}],
content_type: 'bands'
}, {
id: 64,
title: 'SouthPaw',
tags: [{
created_at: '2014-09-13T00:14:51Z',
id: 2,
name: 'Country Stage',
position: 2,
updated_at: '2014-09-13T00:14:51Z'
}],
content_type: 'bands'
}],
total_pages: 1
});
const query = new Query('custom_bands');
query.addSearchValue(1);
query.addSearchValue(2);
query.searchByTagIDMode();
// client.authToken = 'valid_token';
const response = await contentService.filter(query);
t.truthy(response);
t.is(response.length, 4);
t.is(response[0].id, 61);
t.is(response[0].title, 'Almost Journey/2U');
t.truthy(response[0].tags);
t.is(response[0].tags[0].id, 1);
t.is(response[0].contentType.permanentLink, 'bands');
t.is(response[1].id, 62);
t.is(response[1].title, 'Bruce in the USA');
t.truthy(response[1].tags);
t.is(response[1].tags[0].id, 1);
t.is(response[1].contentType.permanentLink, 'bands');
t.is(response[2].id, 63);
t.is(response[2].title, 'Kevin MaC Band');
t.truthy(response[2].tags);
t.is(response[2].tags[0].id, 2);
t.is(response[2].contentType.permanentLink, 'bands');
t.is(response[3].id, 64);
t.is(response[3].title, 'SouthPaw');
t.truthy(response[3].tags);
t.is(response[3].tags[0].id, 2);
t.is(response[3].contentType.permanentLink, 'bands');
});
// ContentService.find method
/**
* @test {ContentService.find}
*/
test('[unit] ContentService.find should throw an error if query is not defined properly', (t) => {
t.throws(() => contentService.find({}), 'ContentService.find error: query is not a type of Query');
});
// /**
// * @test {ContentService.find}
// */
// test('[unit] ContentService.find should call filter with the correct params', async (t) => {
// const filter = sinon.spy(contentService, 'filter');
// const query = new Query('custom_bands');
// query.addSearchValue(1);
// query.addSearchValue(2);
// query.searchByTagIDMode();
// await contentService.find(query);
// t.truthy(filter.called);
// t.truthy(filter.calledWith(query));
// filter.restore();
// });