<commit_before>import Component from '@ember/component';
import {computed} from '@ember/object';
import {reads} from '@ember/object/computed';
import {inject as service} from '@ember/service';
import {set} from '@ember/object';
import {task} from 'ember-concurrency';

const US = {flag: '🇺🇸', name: 'US', baseUrl: 'https://api.mailgun.net/v3'};
const EU = {flag: '🇪🇺', name: 'EU', baseUrl: 'https://api.eu.mailgun.net/v3'};

const CURRENCIES = [
    {
        label: 'USD - US Dollar', value: 'usd'
    },
    {
        label: 'AUD - Australian Dollar', value: 'aud'
    },
    {
        label: 'CAD - Canadian Dollar', value: 'cad'
    },
    {
        label: 'EUR - Euro', value: 'eur'
    },
    {
        label: 'GBP - British Pound', value: 'gbp'
    },
    {
        label: 'INR - Indian Rupee', value: 'inr'
    }
];

export default Component.extend({
    feature: service(),
    config: service(),
    mediaQueries: service(),
    ghostPaths: service(),
    ajax: service(),
    settings: service(),

    currencies: null,
    showFromAddressConfirmation: false,
    showMembersModalSettings: false,

    // passed in actions
    setStripeConnectIntegrationTokenSetting() {},

    defaultContentVisibility: reads('settings.defaultContentVisibility'),

    stripeDirect: reads('config.stripeDirect'),

    allowSelfSignup: reads('settings.membersAllowFreeSignup'),

    /** OLD **/
    stripeDirectPublicKey: reads('settings.stripePublishableKey'),
    stripeDirectSecretKey: reads('settings.stripeSecretKey'),

    stripeConnectAccountId: reads('settings.stripeConnectAccountId'),
    stripeConnectAccountName: reads('settings.stripeConnectDisplayName'),
    stripeConnectLivemode: reads('settings.stripeConnectLivemode'),

    selectedCurrency: computed('stripePlans.monthly.currency', function () {
        return CURRENCIES.findBy('value', this.get('stripePlans.monthly.currency'));
    }),

    disableUpdateFromAddressButton: computed('fromAddress', function () {
        const savedFromAddress = this.get('settings.membersFromAddress') || '';
        if (!savedFromAddress.includes('@') && this.blogDomain) {
            return (this.fromAddress === `${savedFromAddress}@${this.blogDomain}`);
        }
        return (this.fromAddress === savedFromAddress);
    }),

    blogDomain: computed('config.blogDomain', function () {
        let blogDomain = this.config.blogDomain || '';
        const domainExp = blogDomain.replace('https://', '').replace('http://', '').match(new RegExp('^([^/:?#]+)(?:[/:?#]|$)', 'i'));
        return (domainExp && domainExp[1]) || '';
    }),

    mailgunRegion: computed('settings.bulkEmailSettings.baseUrl', function () {
        if (!this.settings.get('bulkEmailSettings.baseUrl')) {
            return US;
        }

        return [US, EU].find((region) => {
            return region.baseUrl === this.settings.get('bulkEmailSettings.baseUrl');
        });
    }),

    stripePlans: computed('settings.stripePlans', function () {
        const plans = this.settings.get('stripePlans');
        const monthly = plans.find(plan => plan.interval === 'month');
        const yearly = plans.find(plan => plan.interval === 'year' && plan.name !== 'Complimentary');

        return {
            monthly: {
                amount: parseInt(monthly.amount) / 100 || 0,
                currency: monthly.currency
            },
            yearly: {
                amount: parseInt(yearly.amount) / 100 || 0,
                yearly: yearly.currency
            }
        };
    }),

    bulkEmailSettings: computed('settings.bulkEmailSettings', function () {
        let bulkEmailSettings = this.get('settings.bulkEmailSettings') || {};
        const {apiKey = '', baseUrl = US.baseUrl, domain = ''} = bulkEmailSettings;
        return {apiKey, baseUrl, domain};
    }),

    hasBulkEmailConfig: computed('settings.bulkEmailSettings', function () {
        let bulkEmailSettings = this.get('settings.bulkEmailSettings');
        return !!bulkEmailSettings.isConfig;
    }),

    init() {
        this._super(...arguments);
        this.set('mailgunRegions', [US, EU]);
        this.set('currencies', CURRENCIES);
    },

    actions: {
        toggleFromAddressConfirmation() {
            this.toggleProperty('showFromAddressConfirmation');
        },

        closeMembersModalSettings() {
            this.set('showMembersModalSettings', false);
        },

        setDefaultContentVisibility(value) {
            this.setDefaultContentVisibility(value);
        },

        setBulkEmailSettings(key, event) {
            let bulkEmailSettings = this.get('settings.bulkEmailSettings') || {};
            bulkEmailSettings[key] = event.target.value;
            if (!bulkEmailSettings.baseUrl) {
                set(bulkEmailSettings, 'baseUrl', US.baseUrl);
            }
            this.setBulkEmailSettings(bulkEmailSettings);
        },

        setBulkEmailRegion(region) {
            let bulkEmailSettings = this.get('settings.bulkEmailSettings') || {};
            set(bulkEmailSettings, 'baseUrl', region.baseUrl);
            this.setBulkEmailSettings(bulkEmailSettings);
        },

        setFromAddress(fromAddress) {
            this.setFromAddress(fromAddress);
        },

        toggleSelfSignup() {
            this.set('settings.membersAllowFreeSignup', !this.get('allowSelfSignup'));
        },

        setStripeDirectPublicKey(event) {
            this.set('settings.stripeProductName', this.get('settings.title'));
            this.set('settings.stripePublishableKey', event.target.value);
        },

        setStripeDirectSecretKey(event) {
            this.set('settings.stripeProductName', this.get('settings.title'));
            this.set('settings.stripeSecretKey', event.target.value);
        },

        setStripePlan(type, event) {
            const updatedPlans = this.get('settings.stripePlans').map((plan) => {
                if (plan.interval === type && plan.name !== 'Complimentary') {
                    const newAmount = parseInt(event.target.value) * 100 || 0;
                    return Object.assign({}, plan, {
                        amount: newAmount
                    });
                }
                return plan;
            });

            this.set('settings.stripePlans', updatedPlans);
        },

        setStripePlansCurrency(event) {
            const newCurrency = event.value;
            const updatedPlans = this.get('settings.stripePlans').map((plan) => {
                if (plan.name !== 'Complimentary') {
                    return Object.assign({}, plan, {
                        currency: newCurrency
                    });
                }
                return plan;
            });

            const currentComplimentaryPlan = updatedPlans.find((plan) => {
                return plan.name === 'Complimentary' && plan.currency === event.value;
            });

            if (!currentComplimentaryPlan) {
                updatedPlans.push({
                    name: 'Complimentary',
                    currency: event.value,
                    interval: 'year',
                    amount: 0
                });
            }

            this.set('settings.stripePlans', updatedPlans);
        },

        setStripeConnectIntegrationToken(event) {
            this.set('settings.stripeProductName', this.get('settings.title'));
            this.setStripeConnectIntegrationTokenSetting(event.target.value);
        },

        openDisconnectStripeModal() {
            this.openDisconnectStripeConnectModal.perform();
        },

        closeDisconnectStripeModal() {
            this.set('showDisconnectStripeConnectModal', false);
        },

        disconnectStripeConnectIntegration() {
            this.disconnectStripeConnectIntegration.perform();
        }
    },

    openDisconnectStripeConnectModal: task(function* () {
        this.set('hasActiveStripeSubscriptions', false);
        if (!this.get('stripeConnectAccountId')) {
            return;
        }
        const url = this.get('ghostPaths.url').api('/members/hasActiveStripeSubscriptions');
        const response = yield this.ajax.request(url);

        if (response.hasActiveStripeSubscriptions) {
            this.set('hasActiveStripeSubscriptions', true);
            return;
        }
        this.set('showDisconnectStripeConnectModal', true);
    }).drop(),

    disconnectStripeConnectIntegration: task(function* () {
        this.set('disconnectStripeError', false);
        const url = this.get('ghostPaths.url').api('/settings/stripe/connect');

        yield this.ajax.delete(url);
        yield this.settings.reload();
    }),

    saveStripeSettings: task(function* () {
        this.set('stripeConnectError', null);
        this.set('stripeConnectSuccess', null);
        if (this.get('settings.stripeConnectIntegrationToken')) {
            try {
                const response = yield this.settings.save();
                this.set('membersStripeOpen', false);
                this.set('stripeConnectSuccess', true);
                return response;
            } catch (error) {
                if (error.payload && error.payload.errors) {
                    this.set('stripeConnectError', 'Invalid secure key');
                    return false;
                }
                throw error;
            }
        } else {
            this.set('stripeConnectError', 'Please enter a secure key');
        }
    }).drop(),

    updateFromAddress: task(function* () {
        let url = this.get('ghostPaths.url').api('/settings/members/email');
        try {
            const response = yield this.ajax.post(url, {
                data: {
                    from_address: this.fromAddress
                }
            });
            this.toggleProperty('showFromAddressConfirmation');
            return response;
        } catch (e) {
            // Failed to send email, retry
            return false;
        }
    }).drop(),

    get liveStripeConnectAuthUrl() {
        return this.ghostPaths.url.api('members/stripe_connect') + '?mode=live';
    },

    get testStripeConnectAuthUrl() {
        return this.ghostPaths.url.api('members/stripe_connect') + '?mode=test';
    }
});
<commit_msg>🐛 Fixed missing currency for yearly plan in Labs subscription price settings (#1626)<commit_after>import Component from '@ember/component';
import {computed} from '@ember/object';
import {reads} from '@ember/object/computed';
import {inject as service} from '@ember/service';
import {set} from '@ember/object';
import {task} from 'ember-concurrency';

const US = {flag: '🇺🇸', name: 'US', baseUrl: 'https://api.mailgun.net/v3'};
const EU = {flag: '🇪🇺', name: 'EU', baseUrl: 'https://api.eu.mailgun.net/v3'};

const CURRENCIES = [
    {
        label: 'USD - US Dollar', value: 'usd'
    },
    {
        label: 'AUD - Australian Dollar', value: 'aud'
    },
    {
        label: 'CAD - Canadian Dollar', value: 'cad'
    },
    {
        label: 'EUR - Euro', value: 'eur'
    },
    {
        label: 'GBP - British Pound', value: 'gbp'
    },
    {
        label: 'INR - Indian Rupee', value: 'inr'
    }
];

export default Component.extend({
    feature: service(),
    config: service(),
    mediaQueries: service(),
    ghostPaths: service(),
    ajax: service(),
    settings: service(),

    currencies: null,
    showFromAddressConfirmation: false,
    showMembersModalSettings: false,

    // passed in actions
    setStripeConnectIntegrationTokenSetting() {},

    defaultContentVisibility: reads('settings.defaultContentVisibility'),

    stripeDirect: reads('config.stripeDirect'),

    allowSelfSignup: reads('settings.membersAllowFreeSignup'),

    /** OLD **/
    stripeDirectPublicKey: reads('settings.stripePublishableKey'),
    stripeDirectSecretKey: reads('settings.stripeSecretKey'),

    stripeConnectAccountId: reads('settings.stripeConnectAccountId'),
    stripeConnectAccountName: reads('settings.stripeConnectDisplayName'),
    stripeConnectLivemode: reads('settings.stripeConnectLivemode'),

    selectedCurrency: computed('stripePlans.monthly.currency', function () {
        return CURRENCIES.findBy('value', this.get('stripePlans.monthly.currency'));
    }),

    disableUpdateFromAddressButton: computed('fromAddress', function () {
        const savedFromAddress = this.get('settings.membersFromAddress') || '';
        if (!savedFromAddress.includes('@') && this.blogDomain) {
            return (this.fromAddress === `${savedFromAddress}@${this.blogDomain}`);
        }
        return (this.fromAddress === savedFromAddress);
    }),

    blogDomain: computed('config.blogDomain', function () {
        let blogDomain = this.config.blogDomain || '';
        const domainExp = blogDomain.replace('https://', '').replace('http://', '').match(new RegExp('^([^/:?#]+)(?:[/:?#]|$)', 'i'));
        return (domainExp && domainExp[1]) || '';
    }),

    mailgunRegion: computed('settings.bulkEmailSettings.baseUrl', function () {
        if (!this.settings.get('bulkEmailSettings.baseUrl')) {
            return US;
        }

        return [US, EU].find((region) => {
            return region.baseUrl === this.settings.get('bulkEmailSettings.baseUrl');
        });
    }),

    stripePlans: computed('settings.stripePlans', function () {
        const plans = this.settings.get('stripePlans');
        const monthly = plans.find(plan => plan.interval === 'month');
        const yearly = plans.find(plan => plan.interval === 'year' && plan.name !== 'Complimentary');

        return {
            monthly: {
                amount: parseInt(monthly.amount) / 100 || 0,
                currency: monthly.currency
            },
            yearly: {
                amount: parseInt(yearly.amount) / 100 || 0,
                currency: yearly.currency
            }
        };
    }),

    bulkEmailSettings: computed('settings.bulkEmailSettings', function () {
        let bulkEmailSettings = this.get('settings.bulkEmailSettings') || {};
        const {apiKey = '', baseUrl = US.baseUrl, domain = ''} = bulkEmailSettings;
        return {apiKey, baseUrl, domain};
    }),

    hasBulkEmailConfig: computed('settings.bulkEmailSettings', function () {
        let bulkEmailSettings = this.get('settings.bulkEmailSettings');
        return !!bulkEmailSettings.isConfig;
    }),

    init() {
        this._super(...arguments);
        this.set('mailgunRegions', [US, EU]);
        this.set('currencies', CURRENCIES);
    },

    actions: {
        toggleFromAddressConfirmation() {
            this.toggleProperty('showFromAddressConfirmation');
        },

        closeMembersModalSettings() {
            this.set('showMembersModalSettings', false);
        },

        setDefaultContentVisibility(value) {
            this.setDefaultContentVisibility(value);
        },

        setBulkEmailSettings(key, event) {
            let bulkEmailSettings = this.get('settings.bulkEmailSettings') || {};
            bulkEmailSettings[key] = event.target.value;
            if (!bulkEmailSettings.baseUrl) {
                set(bulkEmailSettings, 'baseUrl', US.baseUrl);
            }
            this.setBulkEmailSettings(bulkEmailSettings);
        },

        setBulkEmailRegion(region) {
            let bulkEmailSettings = this.get('settings.bulkEmailSettings') || {};
            set(bulkEmailSettings, 'baseUrl', region.baseUrl);
            this.setBulkEmailSettings(bulkEmailSettings);
        },

        setFromAddress(fromAddress) {
            this.setFromAddress(fromAddress);
        },

        toggleSelfSignup() {
            this.set('settings.membersAllowFreeSignup', !this.get('allowSelfSignup'));
        },

        setStripeDirectPublicKey(event) {
            this.set('settings.stripeProductName', this.get('settings.title'));
            this.set('settings.stripePublishableKey', event.target.value);
        },

        setStripeDirectSecretKey(event) {
            this.set('settings.stripeProductName', this.get('settings.title'));
            this.set('settings.stripeSecretKey', event.target.value);
        },

        setStripePlan(type, event) {
            const updatedPlans = this.get('settings.stripePlans').map((plan) => {
                if (plan.interval === type && plan.name !== 'Complimentary') {
                    const newAmount = parseInt(event.target.value) * 100 || 0;
                    return Object.assign({}, plan, {
                        amount: newAmount
                    });
                }
                return plan;
            });

            this.set('settings.stripePlans', updatedPlans);
        },

        setStripePlansCurrency(event) {
            const newCurrency = event.value;
            const updatedPlans = this.get('settings.stripePlans').map((plan) => {
                if (plan.name !== 'Complimentary') {
                    return Object.assign({}, plan, {
                        currency: newCurrency
                    });
                }
                return plan;
            });

            const currentComplimentaryPlan = updatedPlans.find((plan) => {
                return plan.name === 'Complimentary' && plan.currency === event.value;
            });

            if (!currentComplimentaryPlan) {
                updatedPlans.push({
                    name: 'Complimentary',
                    currency: event.value,
                    interval: 'year',
                    amount: 0
                });
            }

            this.set('settings.stripePlans', updatedPlans);
        },

        setStripeConnectIntegrationToken(event) {
            this.set('settings.stripeProductName', this.get('settings.title'));
            this.setStripeConnectIntegrationTokenSetting(event.target.value);
        },

        openDisconnectStripeModal() {
            this.openDisconnectStripeConnectModal.perform();
        },

        closeDisconnectStripeModal() {
            this.set('showDisconnectStripeConnectModal', false);
        },

        disconnectStripeConnectIntegration() {
            this.disconnectStripeConnectIntegration.perform();
        }
    },

    openDisconnectStripeConnectModal: task(function* () {
        this.set('hasActiveStripeSubscriptions', false);
        if (!this.get('stripeConnectAccountId')) {
            return;
        }
        const url = this.get('ghostPaths.url').api('/members/hasActiveStripeSubscriptions');
        const response = yield this.ajax.request(url);

        if (response.hasActiveStripeSubscriptions) {
            this.set('hasActiveStripeSubscriptions', true);
            return;
        }
        this.set('showDisconnectStripeConnectModal', true);
    }).drop(),

    disconnectStripeConnectIntegration: task(function* () {
        this.set('disconnectStripeError', false);
        const url = this.get('ghostPaths.url').api('/settings/stripe/connect');

        yield this.ajax.delete(url);
        yield this.settings.reload();
    }),

    saveStripeSettings: task(function* () {
        this.set('stripeConnectError', null);
        this.set('stripeConnectSuccess', null);
        if (this.get('settings.stripeConnectIntegrationToken')) {
            try {
                const response = yield this.settings.save();
                this.set('membersStripeOpen', false);
                this.set('stripeConnectSuccess', true);
                return response;
            } catch (error) {
                if (error.payload && error.payload.errors) {
                    this.set('stripeConnectError', 'Invalid secure key');
                    return false;
                }
                throw error;
            }
        } else {
            this.set('stripeConnectError', 'Please enter a secure key');
        }
    }).drop(),

    updateFromAddress: task(function* () {
        let url = this.get('ghostPaths.url').api('/settings/members/email');
        try {
            const response = yield this.ajax.post(url, {
                data: {
                    from_address: this.fromAddress
                }
            });
            this.toggleProperty('showFromAddressConfirmation');
            return response;
        } catch (e) {
            // Failed to send email, retry
            return false;
        }
    }).drop(),

    get liveStripeConnectAuthUrl() {
        return this.ghostPaths.url.api('members/stripe_connect') + '?mode=live';
    },

    get testStripeConnectAuthUrl() {
        return this.ghostPaths.url.api('members/stripe_connect') + '?mode=test';
    }
});
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

'use strict';

const fs = require('fs-extra');
const path = require('path');
const os = require('os');
const child_process = require('child_process');
const fetch = require('node-fetch');
const ini = require('ini').parse;
const decompress = require('decompress');

function exec() {
    const args = Array.from(arguments);
    return new Promise((resolve, reject) => {
        function callback(error, stdout, stderr) {
            if (error) {
                reject(error);
            } else {
                resolve(stdout);
            }
        }
        args.push(callback);
        child_process.exec.apply(null, args);
    });
}

function printProgress(input, totalBytes, archive) {
    const ProgressBar = require('progress');
    const StreamCounter = require('stream-counter');

    const message = `Downloading ${archive} [:bar] (:ratek)`;
    const bar = new ProgressBar(message, {
       complete: '=',
       incomplete: ' ',
       width: process.stdout.columns - message.length,
       total: totalBytes / 1024
     });

     input.pipe(new StreamCounter()).on('progress', function() {
         bar.tick((this.bytes / 1024) - bar.curr);
     });
}

function download(serverFolder, archive, destination) {
    const url = `https://static.realm.io/downloads/${serverFolder}/${archive}`;
    return fetch(url).then((response) => {
        if (response.status !== 200) {
            throw new Error(`Error downloading ${url} - received status ${response.status} ${response.statusText}`);
        }

        const lastModified = new Date(response.headers.get('Last-Modified'));
        return fs.exists(destination)
                 .then(exists => {
                     if (!exists) {
                         return saveFile();
                     } else {
                         return fs.stat(destination)
                                  .then(stat => {
                                      if (stat.mtime.getTime() !== lastModified.getTime()) {
                                          return saveFile();
                                      }
                         })
                     }
        });

        function saveFile() {
            if (process.stdout.isTTY) {
                printProgress(response.body, parseInt(response.headers.get('Content-Length')), archive);
            } else {
                console.log(`Downloading ${archive}`);
            }
            return new Promise((resolve) => {
                const file = fs.createWriteStream(destination);
                response.body.pipe(file).once('finish', () => file.close(resolve));
            }).then(() => fs.utimes(destination, lastModified, lastModified));
        }
    });
}

function extract(downloadedArchive, targetFolder, archiveRootFolder) {
    console.log(`Extracting ${path.basename(downloadedArchive)} => ${targetFolder}`);
    const decompressOptions = /tar\.xz$/.test(downloadedArchive) ? { plugins: [ require('decompress-tarxz')() ] } : undefined;
    if (!archiveRootFolder) {
        return decompress(downloadedArchive, targetFolder, decompressOptions);
    } else {
        const tempExtractLocation = path.resolve(os.tmpdir(), path.basename(downloadedArchive, path.extname(downloadedArchive)));
        return decompress(downloadedArchive, tempExtractLocation, decompressOptions)
               .then(() => fs.readdir(path.resolve(tempExtractLocation, archiveRootFolder)))
               .then(items => Promise.all(items.map(item => {
                   const source = path.resolve(tempExtractLocation, archiveRootFolder, item);
                   const target = path.resolve(targetFolder, item);
                   return fs.copy(source, target, { filter: n => path.extname(n) !== '.so' });
               })))
               .then(() => fs.remove(tempExtractLocation))
    }
}

function acquireCore(options, dependencies, target) {
    let serverFolder = `core/v${dependencies.REALM_CORE_VERSION}`;
    let flavor = options.debug ? 'Debug' : 'Release';

    let archive, archiveRootFolder;
    switch (options.platform) {
    case 'mac':
        serverFolder += `/macos/${flavor}`;
        archive = `realm-core-${flavor}-v${dependencies.REALM_CORE_VERSION}-Darwin-devel.tar.gz`;
        break;
    case 'ios':
        flavor = flavor === 'Debug' ? 'MinSizeDebug' : flavor;
        serverFolder += `/ios/${flavor}`;
        archive = `realm-core-${flavor}-v${dependencies.REALM_CORE_VERSION}-iphoneos.tar.gz`;
        break;
    case 'win':
        if (!options.arch) throw new Error(`Specifying '--arch' is required for platform 'win'`);
        const arch = options.arch === 'ia32' ? 'Win32' : options.arch;
        serverFolder += `/windows/${arch}/nouwp/${flavor}`;
        archive = `realm-core-${flavor}-v${dependencies.REALM_CORE_VERSION}-Windows-${arch}-devel.tar.gz`;
        break;
    case 'linux':
        serverFolder = 'core';
        archive = `realm-core-${dependencies.REALM_CORE_VERSION}.tgz`;
        archiveRootFolder = `realm-core-${dependencies.REALM_CORE_VERSION}`;
        break;
    }

    const downloadedArchive = path.resolve(os.tmpdir(), archive);
    return download(serverFolder, archive, downloadedArchive)
           .then(() => extract(downloadedArchive, target, archiveRootFolder));
}

function acquireSync(options, dependencies, target) {
    let serverFolder = 'sync';
    let flavor = options.debug ? 'Debug' : 'Release';

    let archive, archiveRootFolder;
    let promise = Promise.resolve();
    switch (options.platform) {
    case 'mac':
        archive = `realm-sync-node-cocoa-${dependencies.REALM_SYNC_VERSION}.tar.gz`;
        archiveRootFolder = `realm-sync-node-cocoa-${dependencies.REALM_SYNC_VERSION}`;
        break;
    case 'ios':
        archive = `realm-sync-cocoa-${dependencies.REALM_SYNC_VERSION}.tar.xz`;
        archiveRootFolder = `core`;
        break;
    case 'win':
        promise = acquireCore(options, dependencies, target)
                  .then(() => exec(`git ls-remote git@github.com:realm/realm-sync.git --tags "v${dependencies.REALM_SYNC_VERSION}^{}"`))
                  .then(stdout => /([^\t]+)/.exec(stdout)[0])
                  .then(sha => serverFolder += `/sha-version/${sha}`);
        const arch = options.arch === 'ia32' ? 'Win32' : options.arch;
        archive = `realm-sync-${flavor}-v${dependencies.REALM_SYNC_VERSION}-Windows-${arch}-devel.tar.gz`;
        break;
    default:
        throw new Error(`Unsupported sync platform '${options.platform}'`);
    }

    const downloadedArchive = path.resolve(os.tmpdir(), archive);
    return promise
           .then(() => download(serverFolder, archive, downloadedArchive))
           .then(() => extract(downloadedArchive, target, archiveRootFolder));
}

const optionDefinitions = [
    { name: 'platform', type: String, defaultOption: true },
    { name: 'arch', type: String },
    { name: 'sync', type: Boolean },
    { name: 'debug', type: Boolean },
];
const options = require('command-line-args')(optionDefinitions);
if (options.platform === '..\\win') {
    options.platform = 'win'; // handle gyp idiocy
}

const vendorDir = path.resolve(__dirname, '../vendor');

let realmDir = path.resolve(vendorDir, `realm-${options.platform}`);
if (options.arch) {
    realmDir += `-${options.arch}`;
}
if (options.debug) {
    realmDir += '-dbg'
}

const dependencies = ini(fs.readFileSync(path.resolve(__dirname, '../dependencies.list'), 'utf8'));

const acquire = options.sync ? acquireSync : acquireCore;
fs.emptyDir(realmDir)
  .then(() => acquire(options, dependencies, realmDir))
  .catch(error => console.error(error));
<commit_msg>fix download-realm to account for lightweight tags<commit_after>////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////

'use strict';

const fs = require('fs-extra');
const path = require('path');
const os = require('os');
const child_process = require('child_process');
const fetch = require('node-fetch');
const ini = require('ini').parse;
const decompress = require('decompress');

function exec() {
    const args = Array.from(arguments);
    return new Promise((resolve, reject) => {
        function callback(error, stdout, stderr) {
            if (error) {
                reject(error);
            } else {
                resolve(stdout.trim());
            }
        }
        args.push(callback);
        child_process.exec.apply(null, args);
    });
}

function printProgress(input, totalBytes, archive) {
    const ProgressBar = require('progress');
    const StreamCounter = require('stream-counter');

    const message = `Downloading ${archive} [:bar] (:ratek)`;
    const bar = new ProgressBar(message, {
       complete: '=',
       incomplete: ' ',
       width: process.stdout.columns - message.length,
       total: totalBytes / 1024
     });

     input.pipe(new StreamCounter()).on('progress', function() {
         bar.tick((this.bytes / 1024) - bar.curr);
     });
}

function download(serverFolder, archive, destination) {
    const url = `https://static.realm.io/downloads/${serverFolder}/${archive}`;
    return fetch(url).then((response) => {
        if (response.status !== 200) {
            throw new Error(`Error downloading ${url} - received status ${response.status} ${response.statusText}`);
        }

        const lastModified = new Date(response.headers.get('Last-Modified'));
        return fs.exists(destination)
                 .then(exists => {
                     if (!exists) {
                         return saveFile();
                     } else {
                         return fs.stat(destination)
                                  .then(stat => {
                                      if (stat.mtime.getTime() !== lastModified.getTime()) {
                                          return saveFile();
                                      }
                         })
                     }
        });

        function saveFile() {
            if (process.stdout.isTTY) {
                printProgress(response.body, parseInt(response.headers.get('Content-Length')), archive);
            } else {
                console.log(`Downloading ${archive}`);
            }
            return new Promise((resolve) => {
                const file = fs.createWriteStream(destination);
                response.body.pipe(file).once('finish', () => file.close(resolve));
            }).then(() => fs.utimes(destination, lastModified, lastModified));
        }
    });
}

function extract(downloadedArchive, targetFolder, archiveRootFolder) {
    console.log(`Extracting ${path.basename(downloadedArchive)} => ${targetFolder}`);
    const decompressOptions = /tar\.xz$/.test(downloadedArchive) ? { plugins: [ require('decompress-tarxz')() ] } : undefined;
    if (!archiveRootFolder) {
        return decompress(downloadedArchive, targetFolder, decompressOptions);
    } else {
        const tempExtractLocation = path.resolve(os.tmpdir(), path.basename(downloadedArchive, path.extname(downloadedArchive)));
        return decompress(downloadedArchive, tempExtractLocation, decompressOptions)
               .then(() => fs.readdir(path.resolve(tempExtractLocation, archiveRootFolder)))
               .then(items => Promise.all(items.map(item => {
                   const source = path.resolve(tempExtractLocation, archiveRootFolder, item);
                   const target = path.resolve(targetFolder, item);
                   return fs.copy(source, target, { filter: n => path.extname(n) !== '.so' });
               })))
               .then(() => fs.remove(tempExtractLocation))
    }
}

function acquireCore(options, dependencies, target) {
    let serverFolder = `core/v${dependencies.REALM_CORE_VERSION}`;
    let flavor = options.debug ? 'Debug' : 'Release';

    let archive, archiveRootFolder;
    switch (options.platform) {
    case 'mac':
        serverFolder += `/macos/${flavor}`;
        archive = `realm-core-${flavor}-v${dependencies.REALM_CORE_VERSION}-Darwin-devel.tar.gz`;
        break;
    case 'ios':
        flavor = flavor === 'Debug' ? 'MinSizeDebug' : flavor;
        serverFolder += `/ios/${flavor}`;
        archive = `realm-core-${flavor}-v${dependencies.REALM_CORE_VERSION}-iphoneos.tar.gz`;
        break;
    case 'win':
        if (!options.arch) throw new Error(`Specifying '--arch' is required for platform 'win'`);
        const arch = options.arch === 'ia32' ? 'Win32' : options.arch;
        serverFolder += `/windows/${arch}/nouwp/${flavor}`;
        archive = `realm-core-${flavor}-v${dependencies.REALM_CORE_VERSION}-Windows-${arch}-devel.tar.gz`;
        break;
    case 'linux':
        serverFolder = 'core';
        archive = `realm-core-${dependencies.REALM_CORE_VERSION}.tgz`;
        archiveRootFolder = `realm-core-${dependencies.REALM_CORE_VERSION}`;
        break;
    }

    const downloadedArchive = path.resolve(os.tmpdir(), archive);
    return download(serverFolder, archive, downloadedArchive)
           .then(() => extract(downloadedArchive, target, archiveRootFolder));
}

function getSyncCommitSha(version) {
  return exec(`git ls-remote git@github.com:realm/realm-sync.git --tags "v${version}^{}"`)
         .then(stdout => {
           if (!Boolean(stdout)) {
             return exec(`git ls-remote git@github.com:realm/realm-sync.git --tags "v${version}"`)
           } else {
             return stdout;
           }
         }).then(stdout => /([^\t]+)/.exec(stdout)[0]);
}

function acquireSync(options, dependencies, target) {
    let serverFolder = 'sync';
    let flavor = options.debug ? 'Debug' : 'Release';

    let archive, archiveRootFolder;
    let promise = Promise.resolve();
    switch (options.platform) {
    case 'mac':
        archive = `realm-sync-node-cocoa-${dependencies.REALM_SYNC_VERSION}.tar.gz`;
        archiveRootFolder = `realm-sync-node-cocoa-${dependencies.REALM_SYNC_VERSION}`;
        break;
    case 'ios':
        archive = `realm-sync-cocoa-${dependencies.REALM_SYNC_VERSION}.tar.xz`;
        archiveRootFolder = `core`;
        break;
    case 'win':
        promise = acquireCore(options, dependencies, target)
                  .then(() => getSyncCommitSha(dependencies.REALM_SYNC_VERSION))
                  .then(sha => serverFolder += `/sha-version/${sha}`);
        const arch = options.arch === 'ia32' ? 'Win32' : options.arch;
        archive = `realm-sync-${flavor}-v${dependencies.REALM_SYNC_VERSION}-Windows-${arch}-devel.tar.gz`;
        break;
    default:
        throw new Error(`Unsupported sync platform '${options.platform}'`);
    }

    const downloadedArchive = path.resolve(os.tmpdir(), archive);
    return promise
           .then(() => download(serverFolder, archive, downloadedArchive))
           .then(() => extract(downloadedArchive, target, archiveRootFolder));
}

const optionDefinitions = [
    { name: 'platform', type: String, defaultOption: true },
    { name: 'arch', type: String },
    { name: 'sync', type: Boolean },
    { name: 'debug', type: Boolean },
];
const options = require('command-line-args')(optionDefinitions);
if (options.platform === '..\\win') {
    options.platform = 'win'; // handle gyp idiocy
}

const vendorDir = path.resolve(__dirname, '../vendor');

let realmDir = path.resolve(vendorDir, `realm-${options.platform}`);
if (options.arch) {
    realmDir += `-${options.arch}`;
}
if (options.debug) {
    realmDir += '-dbg'
}

const dependencies = ini(fs.readFileSync(path.resolve(__dirname, '../dependencies.list'), 'utf8'));

const acquire = options.sync ? acquireSync : acquireCore;
fs.emptyDir(realmDir)
  .then(() => acquire(options, dependencies, realmDir))
  .catch(error => console.error(error));
<|endoftext|>


<commit_before>module.exports = function(robot) {

	var slack_msg = 'slack:msg_action:';

  	robot.on(slack_msg + 'wopr_game', function(data, res) {

	    var response_url = data.response_url;
	    console.log(data.response_url);
	    var slackMsg = require('./slackMsgs');
	    var response = slackMsg.basicMessage();
		res.send(response);
	});

  	robot.on(slack_msg + 'trello_board', function(data, res) {
		res.send('trello board button pressed');

	    var msg = 'trello_board_list';
	    k = res;
	    return robot.emit(msg, 'data', k);
	    //var handled = robot.emit(msg, 'data', res);
	    // if (!handled) {
	    //   //res.send(500)
	    //   res.send('\nNo scripts handled the action.\n');
	    // }
		// //console.log(data);
	});

}
<commit_msg>update slack callback<commit_after>module.exports = function(robot) {

	var slack_msg = 'slack:msg_action:';

  	robot.on(slack_msg + 'wopr_game', function(data, res) {

	    var response_url = data.response_url;
	    console.log(data.response_url);
	    var slackMsg = require('./slackMsgs');
	    var response = slackMsg.basicMessage();
		res.send(response);
	});

 //  	robot.on(slack_msg + 'trello_board', function(data, res) {
	// 	res.send('trello board button pressed');

	//     var msg = 'trello_board_list';
	//     k = res;
	//     return robot.emit(msg, 'data', k);
	//     //var handled = robot.emit(msg, 'data', res);
	//     // if (!handled) {
	//     //   //res.send(500)
	//     //   res.send('\nNo scripts handled the action.\n');
	//     // }
	// 	// //console.log(data);
	// });

}
<|endoftext|>


<commit_before>(function () {
    // a stupidly simple copy button
    // - copies on awesome browsers/devices
    // - selects text on underwhelming mobile devices
    var copyBtn = document.querySelector('.js-copy-btn');
    copyBtn.addEventListener('click', function (event) {
        // select the text
        var copyItem = document.querySelector('.text-to-copy');
        var range = document.createRange();
        range.selectNode(copyItem);
        window.getSelection().addRange(range);
        try {
            // now that we've selected the text, execute the copy command
            var successful = document.execCommand('copy');
            if ((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)) || (navigator.userAgent.match(/iPad/i))) {
                copyBtn.textContent = "Now click 'Copy'";
            } else {
                copyBtn.textContent = "Copied!";
            }
        } catch (err) {
            copyBtn.textContent = "Epic fail!";
        }
        var selection = window.getSelection();
        range.selectNodeContents(copyItem);
        selection.removeAllRanges();
        selection.addRange(range);
        copyItem.focus();
        copyItem.setSelectionRange(0, 9999);
    });
})();
<commit_msg>add safari detection<commit_after>(function () {
    // a stupidly simple copy button
    // - copies on awesome browsers/devices
    // - selects text on underwhelming mobile devices
    var copyBtn = document.querySelector('.js-copy-btn');
    copyBtn.addEventListener('click', function (event) {
        // select the text
        var copyItem = document.querySelector('.text-to-copy');
        var range = document.createRange();
        range.selectNode(copyItem);
        window.getSelection().addRange(range);
        try {
            // now that we've selected the text, execute the copy command
            var successful = document.execCommand('copy');
            if ((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)) || (navigator.userAgent.match(/iPad/i))) {
                copyBtn.textContent = "Now click 'Copy'";
            } else if (/^((?!chrome).)*safari/i.test(navigator.userAgent)) {
                // fancy safari detection thanks to: http://stackoverflow.com/a/23522755
                copyBtn.textContent = "Now press Command + C to copy";
            }
            else {
                copyBtn.textContent = "Copied!";
            }
        } catch (err) {
            copyBtn.textContent = "Epic fail!";
        }
        var selection = window.getSelection();
        range.selectNodeContents(copyItem);
        selection.removeAllRanges();
        selection.addRange(range);
        copyItem.focus();
        copyItem.setSelectionRange(0, 9999);
    });
})();
<|endoftext|>


<commit_before>describe("My demo suite", function() {
	it("contains spec with my demo expectation", function() {
		expect(1).toBe(1);
	});
});

describe("My suite is just a function", function() {
	var a = false;

	it("and so is a spec", function(){
		expect(a).toBe(false);
	});

	it("the spec is using not matcher", function() {
		expect(a).not.toBe(true);
		expect(a).not.toBe(null);
	});
});

describe("My suite on toBe", function() {
	it("also work for object", function() {
		var person = {
			name: "Philip",
			age: 36
		};

		var me = {
			name: "Philip",
			age: 36
		};

		expect(person).toEqual(me);
	});

	it("works for regular expressions the same", function(){
		var msg = "philip cen dai";

		expect(msg).toMatch(/cen/);
		expect(msg).toMatch(/Dai$/i);
		expect(msg).not.toMatch(/developer/);
	});

	it("Test on 'toBeDefined' matcher compare against 'undefined'", function() {
		var a = {
			foo: "foo"
		};

		expect(a.foo).toBeDefined();
		expect(a.bar).toBeUndefined();

	});

	it("Test on 'toBeNull' matcher compare against null", function() {
		var a = null;
		var foo = "foo";

		expect(null).toBeNull();
		expect(a).toBeNull();
		expect(foo).not.toBeNull();
	});

	it("Test on 'toBeTruthy'", function() {
		var a, me = "Philip Dai";
		expect(me).toBeTruthy();
		expect(a).not.toBeTruthy();
	});

	it("Test on 'toBeFalsy'", function() {
		var a, me = "Philip Dai";
		expect(a).toBeFalsy();
		expect(me).not.toBeFalsy();
	});

	it("Test on 'toContain'", function() {
		var arr = ['Philip', 'Cen', 'Dai'];

		expect(arr).toContain('Philip');
		expect(arr).not.toContain('Developer');
	});

	it("Test 'toBeLessThan and toBeGreatThan'", function() {
		var one = 1, two = 2;
		expect(one).toBeLessThan(two);
		expect(two).toBeGreaterThan(one);
	});

	it("Test 'toThrowError' matcher", function() {
		var foo = function() {
			return 2 + 1;
		};

		var bar = function() {
			return a + 1;
		};

		expect(foo).not.toThrow();
		expect(bar).toThrow();
	});
});
<commit_msg>Test toBeCloseTo<commit_after>describe("My demo suite", function() {
	it("contains spec with my demo expectation", function() {
		expect(1).toBe(1);
	});
});

describe("My suite is just a function", function() {
	var a = false;

	it("and so is a spec", function(){
		expect(a).toBe(false);
	});

	it("the spec is using not matcher", function() {
		expect(a).not.toBe(true);
		expect(a).not.toBe(null);
	});
});

describe("My suite on toBe", function() {
	it("also work for object", function() {
		var person = {
			name: "Philip",
			age: 36
		};

		var me = {
			name: "Philip",
			age: 36
		};

		expect(person).toEqual(me);
	});

	it("works for regular expressions the same", function(){
		var msg = "philip cen dai";

		expect(msg).toMatch(/cen/);
		expect(msg).toMatch(/Dai$/i);
		expect(msg).not.toMatch(/developer/);
	});

	it("Test on 'toBeDefined' matcher compare against 'undefined'", function() {
		var a = {
			foo: "foo"
		};

		expect(a.foo).toBeDefined();
		expect(a.bar).toBeUndefined();

	});

	it("Test on 'toBeNull' matcher compare against null", function() {
		var a = null;
		var foo = "foo";

		expect(null).toBeNull();
		expect(a).toBeNull();
		expect(foo).not.toBeNull();
	});

	it("Test on 'toBeTruthy'", function() {
		var a, me = "Philip Dai";
		expect(me).toBeTruthy();
		expect(a).not.toBeTruthy();
	});

	it("Test on 'toBeFalsy'", function() {
		var a, me = "Philip Dai";
		expect(a).toBeFalsy();
		expect(me).not.toBeFalsy();
	});

	it("Test on 'toContain'", function() {
		var arr = ['Philip', 'Cen', 'Dai'];

		expect(arr).toContain('Philip');
		expect(arr).not.toContain('Developer');
	});

	it("Test 'toBeLessThan and toBeGreatThan'", function() {
		var one = 1, two = 2;
		expect(one).toBeLessThan(two);
		expect(two).toBeGreaterThan(one);
	});

	it("Test 'toThrowError' matcher", function() {
		var foo = function() {
			return 2 + 1;
		};

		var bar = function() {
			return a + 1;
		};

		expect(foo).not.toThrow();
		expect(bar).toThrow();
	});

	it("Test 'toBeCloseTo'", function() {
		var pi = 3.14,
				e = 2.78;

		expect(pi).not.toBeCloseTo(e, 2);
		expect(pi).toBeCloseTo(e, 0);
	});
});
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import {
  BrowserRouter as Router,
  Route,
  Switch,
  Redirect
} from 'react-router-dom';
import cx from 'classnames';
import PageHeader from 'components/PageHeader';
import PageFooter from 'components/PageFooter';
import SideNav from 'components/SideNav';
import FooterNav from 'components/FooterNav';
import ScrollToTop from 'components/ScrollToTop';

import packageJson from '../../packages/zent/package.json';
import navData from './nav.config';
import { registerRoute, registerFooter } from './router.config';
import { prefix } from './constants';

// one-dimentional array
const routeData = registerRoute(navData['zh-CN']);

// double-linked list
const footerData = registerFooter(routeData);

export default class App extends Component {
  state = {
    spiderOn: false,
    spiderReady: false
  };

  render() {
    const { spiderOn, spiderReady } = this.state;

    return (
      <Router>
        <ScrollToTop>
          <PageHeader version={packageJson.version} />

              <SideNav
                data={navData['zh-CN']}
                base={prefix}
                ref={this.saveSideNav}
              />

                    {spiderReady && (

                    )}

                  <Switch>
                    {routeData.map((data, index) => {
                      return (
                        <Route
                          key={`route-${index}`}
                          component={data.component}
                          path={data.path}
                        />
                      );
                    })}

                    <Redirect from="*" to={routeData[0].path} />
                  </Switch>

                <FooterNav data={footerData} />

          <PageFooter ref={this.saveFooter} />
        </ScrollToTop>
      </Router>
    );
  }

  onGithubSpiderMouseEnter = () => {
    this.setState({
      spiderReady: true,
      spiderOn: true
    });
  };

  saveSpiderNode = node => {
    this.spiderNode = node;
  };

  onMouseMove = evt => {
    const { target } = evt;
    if (!this.spiderNode || !this.spiderNode.contains(target)) {
      this.setState({
        spiderOn: false
      });
    }
  };

  componentDidMount() {
    window.addEventListener('mousemove', this.onMouseMove);
  }

  componentWillUnmount() {
    window.removeEventListener('mousemove', this.onMouseMove);
  }
}
<commit_msg>Throttle mousemove event<commit_after>import React, { Component } from 'react';
import {
  BrowserRouter as Router,
  Route,
  Switch,
  Redirect
} from 'react-router-dom';
import cx from 'classnames';
import PageHeader from 'components/PageHeader';
import PageFooter from 'components/PageFooter';
import SideNav from 'components/SideNav';
import FooterNav from 'components/FooterNav';
import ScrollToTop from 'components/ScrollToTop';
import throttle from 'lodash/throttle';

import packageJson from '../../packages/zent/package.json';
import navData from './nav.config';
import { registerRoute, registerFooter } from './router.config';
import { prefix } from './constants';

// one-dimentional array
const routeData = registerRoute(navData['zh-CN']);

// double-linked list
const footerData = registerFooter(routeData);

export default class App extends Component {
  state = {
    spiderOn: false,
    spiderReady: false
  };

  render() {
    const { spiderOn, spiderReady } = this.state;

    return (
      <Router>
        <ScrollToTop>
          <PageHeader version={packageJson.version} />

              <SideNav
                data={navData['zh-CN']}
                base={prefix}
                ref={this.saveSideNav}
              />

                    {spiderReady && (

                    )}

                  <Switch>
                    {routeData.map((data, index) => {
                      return (
                        <Route
                          key={`route-${index}`}
                          component={data.component}
                          path={data.path}
                        />
                      );
                    })}

                    <Redirect from="*" to={routeData[0].path} />
                  </Switch>

                <FooterNav data={footerData} />

          <PageFooter ref={this.saveFooter} />
        </ScrollToTop>
      </Router>
    );
  }

  onGithubSpiderMouseEnter = () => {
    this.setState({
      spiderReady: true,
      spiderOn: true
    });
  };

  saveSpiderNode = node => {
    this.spiderNode = node;
  };

  onMouseMove = throttle(evt => {
    const { spiderReady, spiderOn } = this.state;
    if (!spiderReady || !spiderOn) {
      return;
    }

    const { target } = evt;
    if (!this.spiderNode || !this.spiderNode.contains(target)) {
      this.setState({
        spiderOn: false
      });
    }
  }, 16);

  componentDidMount() {
    window.addEventListener('mousemove', this.onMouseMove);
  }

  componentWillUnmount() {
    window.removeEventListener('mousemove', this.onMouseMove);
  }
}
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import ReactTestUtils from 'react-addons-test-utils';
import ReactAudioPlayer from '../src/index.jsx';
import song from './fixtures/turkish_march.ogg';

describe('ReactAudioPlayer', function() {
  it('renders an audio element', function() {
    const instance = ReactTestUtils.renderIntoDocument(
      <ReactAudioPlayer />
    );

    const instanceEl = ReactDOM.findDOMNode(instance);

    expect(instanceEl.tagName).toBe('AUDIO');
  });

  it('sets the loop attribute if provided', function() {
    const instance = ReactTestUtils.renderIntoDocument(
      <ReactAudioPlayer
        src={song}
        loop="loop"
      />
    );

    const instanceEl = ReactDOM.findDOMNode(instance);

    expect(instanceEl.getAttribute('loop')).not.toBe(null);
  })

  it('sets title', function() {
    const instance = ReactTestUtils.renderIntoDocument(
      <ReactAudioPlayer
        src={song}
        loop={false}
        title="Turkish march"
      />
    );

    const instanceEl = ReactDOM.findDOMNode(instance);
    console.log(instanceEl.getAttribute("title"));
    console.log(instanceEl.getAttribute("src"));
    expect(instanceEl.getAttribute("title")).toBe("Turkish march");

  })

  it('receives all custom props', function() {
    const instance = ReactTestUtils.renderIntoDocument(
      <ReactAudioPlayer
        src={song}
        loop="loop"
        name="custom-name"
        data-id="custom-data"
      />
    );

    const props = Object.keys(instance.props);

    expect(props).toContain('name');
    expect(props).toContain('data-id');
  })

  describe('when can play', function() {
    it('calls onCanPlay', function(done) {
      var onCanPlay = jasmine.createSpy('onCanPlay').and.callFake(function() {
        expect(onCanPlay).toHaveBeenCalled();
        done();
      });

      ReactTestUtils.renderIntoDocument(
        <ReactAudioPlayer
          src={song}
          autoPlay={true}
          onCanPlay={onCanPlay}
        />
      );
    });
  });

  describe('when can play the entire file', function() {
    it('calls onCanPlayThrough', function(done) {
      var onCanPlayThrough = jasmine.createSpy('onCanPlayThrough').and.callFake(function() {
        expect(onCanPlayThrough).toHaveBeenCalled();
        done();
      });

      ReactTestUtils.renderIntoDocument(
        <ReactAudioPlayer
          src={song}
          autoPlay={true}
          onCanPlayThrough={onCanPlayThrough}
        />
      );
    });
  });
});
<commit_msg>Remove console logs<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import ReactTestUtils from 'react-addons-test-utils';
import ReactAudioPlayer from '../src/index.jsx';
import song from './fixtures/turkish_march.ogg';

describe('ReactAudioPlayer', function() {
  it('renders an audio element', function() {
    const instance = ReactTestUtils.renderIntoDocument(
      <ReactAudioPlayer />
    );

    const instanceEl = ReactDOM.findDOMNode(instance);

    expect(instanceEl.tagName).toBe('AUDIO');
  });

  it('sets the loop attribute if provided', function() {
    const instance = ReactTestUtils.renderIntoDocument(
      <ReactAudioPlayer
        src={song}
        loop="loop"
      />
    );

    const instanceEl = ReactDOM.findDOMNode(instance);

    expect(instanceEl.getAttribute('loop')).not.toBe(null);
  })

  it('sets title', function() {
    const instance = ReactTestUtils.renderIntoDocument(
      <ReactAudioPlayer
        src={song}
        loop={false}
        title="Turkish march"
      />
    );

    const instanceEl = ReactDOM.findDOMNode(instance);

    expect(instanceEl.getAttribute("title")).toBe("Turkish march");
  })

  it('receives all custom props', function() {
    const instance = ReactTestUtils.renderIntoDocument(
      <ReactAudioPlayer
        src={song}
        loop="loop"
        name="custom-name"
        data-id="custom-data"
      />
    );

    const props = Object.keys(instance.props);

    expect(props).toContain('name');
    expect(props).toContain('data-id');
  })

  describe('when can play', function() {
    it('calls onCanPlay', function(done) {
      var onCanPlay = jasmine.createSpy('onCanPlay').and.callFake(function() {
        expect(onCanPlay).toHaveBeenCalled();
        done();
      });

      ReactTestUtils.renderIntoDocument(
        <ReactAudioPlayer
          src={song}
          autoPlay={true}
          onCanPlay={onCanPlay}
        />
      );
    });
  });

  describe('when can play the entire file', function() {
    it('calls onCanPlayThrough', function(done) {
      var onCanPlayThrough = jasmine.createSpy('onCanPlayThrough').and.callFake(function() {
        expect(onCanPlayThrough).toHaveBeenCalled();
        done();
      });

      ReactTestUtils.renderIntoDocument(
        <ReactAudioPlayer
          src={song}
          autoPlay={true}
          onCanPlayThrough={onCanPlayThrough}
        />
      );
    });
  });
});
<|endoftext|>


<commit_before>describe('Sieve', function(){
    it('should be defined', function(){
	expect(Sieve).toBeDefined();
    });

    describe('Number', function(){
	var testValue = 12;
	var number;

	beforeEach(function(){
	    number = new Sieve.Number(testValue);
	});

	it('should be defined', function(){
	    expect(Sieve.Number).toBeDefined();
	});

	it('should be observable', function(){
	    var n = new Sieve.Number(2);

	    expect(n instanceof Observable).toBeTruthy();
	});

	it('should have a value', function(){
	    expect(number.value()).toBe(testValue)
	});

	it('should start with an empty list of divisors', function(){
	    expect(number.divisors().length).toBe(0);
	});

	it('should add divisors', function(){
	    number.addDivisor(2);

	    expect(number.divisors()).toContain(2);

	    number.addDivisor(3);

	    expect(number.divisors()).toContain(2);
	    expect(number.divisors()).toContain(3);
	});

	it('should notify when a divisor is added', function(){
	    var isCalled = false;
	    number.addObserver(function(){ isCalled = true; });

	    number.addDivisor(2);

	    expect(isCalled).toBeTruthy();
	});
    });

    describe('Model', function(){
	var n = 20;
	var model;

	beforeEach(function(){
	    model = new Sieve.Model(n);
	});

	it('should be defined', function(){
	    expect(Sieve.Model).toBeDefined();
	});

 	it('should be observable', function(){
	    var m = new Sieve.Model(10);

	    expect(m instanceof Observable).toBeTruthy();
	});

	it('should contain numbers', function(){
	    expect(model.numbers().length).toBe(n);
	});

	it('number on should contain the special divisor \'0\'', function(){
	    expect(model.numbers()[0].divisors()).toContain(0);
	});

	it('should start out not finished', function(){
	    expect(model.finished()).toBeFalsy();
	});

	it('sieveStep should mark multiples of first unrecognized prime', function(){
	    model.sieveStep();

	    var numbers = model.numbers();
	    [4, 6, 8, 10, 12, 14, 16, 18, 20].forEach(function(index){
		expect(numbers[index - 1].divisors()).toContain(2);
	    });
	});

	it('consequtive sieveStep should mark multiples of consequtive primes', function(){
	    model.sieveStep();
	    model.sieveStep();
	    model.sieveStep();

	    var numbers = model.numbers();
	    [4, 6, 8, 10, 12, 14, 16, 18, 20].forEach(function(index){
		expect(numbers[index - 1].divisors()).toContain(2);
	    });
	    [6, 9, 12, 15, 18].forEach(function(index){
		expect(numbers[index - 1].divisors()).toContain(3);
	    });
	    [10, 15, 20].forEach(function(index){
		expect(numbers[index - 1].divisors()).toContain(5);
	    });
	});

	it('should be finished if no candidates can have divisors', function(){
	    expect(model.finished()).toBeFalsy();
	    model.sieveStep();

	    expect(model.finished()).toBeFalsy();
	    model.sieveStep();

	    expect(model.finished()).toBeFalsy();
	    model.sieveStep();

	    expect(model.finished()).toBeFalsy();
	    model.sieveStep();

	    expect(model.finished()).toBeTruthy();
	});

	it('sieve should sieveStep until finished', function(){
	    model.sieve();
	    var numbers = model.numbers();
	    [
		{ divisor: 2, multiples: [4, 6, 8, 10, 12, 14, 16, 18, 20] },
		{ divisor: 3, multiples: [6, 9, 12, 15] },
		{ divisor: 5, multiples: [10, 15] },
		{ divisor: 7, multiples: [14] },
	    ].forEach(function(entry) {
		entry.multiples.forEach(function(index) {
		    expect(numbers[index - 1].divisors()).toContain(entry.divisor);
		});
	    });

	    [2, 3, 5, 7, 11, 13, 17, 19].forEach(function(prime) {
		expect(numbers[prime - 1].divisors().length).toBe(0);
	    });
n	});
    });

    describe('Views', function(){
	beforeEach(function(){
	    var container = document.createElement('div');
	    container.setAttribute('id', 'test-sieve-container');
	    var body = document.getElementsByTagName('body')[0];
	    body.appendChild(container);
	});

	describe('Setup', function(){
	    it('should have created a test container', function(){
		var testContainer = document.getElementById('test-sieve-container');

		expect(testContainer).toBeDefined();
	    });
	});

	describe('Number', function(){
	    var parent;

	    beforeEach(function(){
		parent = document.createElement('span');
		document.getElementById('test-sieve-container').appendChild(parent);
	    });

	    it('should be defined', function(){
		expect(Sieve.NumberView).toBeDefined();
	    });

	    it('should represent the model', function(){
		var n = new Sieve.Number(4);

		new Sieve.NumberView(parent, n);

		expect(parent.textContent).toBe('4');
	    });

	    it('should strike through if model has divisors', function(){
		var n = new Sieve.Number(4);
		n.addDivisor(2);

		new Sieve.NumberView(parent, n);

		expect(parent.style['text-decoration']).toBe('line-through');
	    });

	    it('should strike through when the model acquires divisors', function(){
		var n = new Sieve.Number(4);
		new Sieve.NumberView(parent, n);
		expect(parent.style['text-decoration']).toBe('none');

		n.addDivisor(2);

		expect(parent.style['text-decoration']).toBe('line-through');
	    });
	});

	describe('View', function(){
	    var n = 5;
	    var parent;
	    var model;

	    beforeEach(function(){
		parent = document.createElement('span');
		parent.textContent = 'original';
		document.getElementById('test-sieve-container').appendChild(parent);
	    });

	    beforeEach(function(){
		model = new Sieve.Model(n);
	    });

	    it('should be defined', function(){
		expect(Sieve.View).toBeDefined();
	    });

 	    it('should represent the model', function(){
		new Sieve.View(parent, model);

		expect(parent.children.length).toBe(n);
	    });

	    it('should represent the numbers', function(){
		new Sieve.View(parent, model);

		[1, 2, 3, 4, 5].forEach(function(p){
		    expect(parent.children[p-1].textContent).toBe('' + p);
		});
	    });

	    it('should mark the current index', function(){
		var view = new Sieve.View(parent, model);
		var currentIndex = model.currentIndex;

		view.children().forEach(function(container, index) {
		    var classAttribute = container.getAttribute('class');
		    console.log(container.getAttribute('class'));
		    if (index === currentIndex) {
			expect(classAttribute).toBe('current');
		    } else {
			expect(classAttribute).toBe('');
		    }
		});
	    });

	    it('should update the current index after sieveStep', function(){
		var view = new Sieve.View(parent, model);

		model.sieveStep();
		var currentIndex = model.currentIndex;

		view.children().forEach(function(container, index) {
		    var classAttribute = container.getAttribute('class');
		    console.log(container.getAttribute('class'));
		    if (index === currentIndex) {
			expect(classAttribute).toBe('current');
		    } else {
			expect(classAttribute).toBe('');
		    }
		});
	    });

	    it('should clear the content', function(){
		var view = new Sieve.View(parent, model);

		expect(parent.textContent).not.toContain('original');
	    });
	});

	describe('ControlView', function(){
	    var n = 7;
	    var parent;
	    var model;

	    beforeEach(function(){
		parent = document.createElement('span');
		parent.textContent = 'original';
		document.getElementById('test-sieve-container').appendChild(parent);
	    });

	    beforeEach(function(){
		model = new Sieve.Model(n);
	    });

	    it('should be defined', function(){
		expect(Sieve.ControlView).toBeDefined();
	    });

 	    it('should have controls and container', function(){
		new Sieve.ControlView(parent, model);

		expect(parent.children.length).toBe(3);
	    });

	    it('should represent the numbers', function(){
		new Sieve.ControlView(parent, model);

		[1, 2, 3, 4, 5].forEach(function(p){
		    expect(parent.children[2].children[p-1].textContent).toBe('' + p);
		});
	    });

	    it('should represent the controls', function(){
		new Sieve.ControlView(parent, model);

		expect(parent.children[0].textContent).toBe('>');
		expect(parent.children[1].textContent).toBe('>>');
	    });

	    it('should clear the content', function(){
		var view = new Sieve.ControlView(parent, model);

		expect(parent.textContent).not.toContain('original');
	    });
	});

	afterEach(function(){
	    var container = document.getElementById('test-sieve-container');
	    container.parentNode.removeChild(container);
	});
    });

});
<commit_msg>remove superfluous console.log<commit_after>describe('Sieve', function(){
    it('should be defined', function(){
	expect(Sieve).toBeDefined();
    });

    describe('Number', function(){
	var testValue = 12;
	var number;

	beforeEach(function(){
	    number = new Sieve.Number(testValue);
	});

	it('should be defined', function(){
	    expect(Sieve.Number).toBeDefined();
	});

	it('should be observable', function(){
	    var n = new Sieve.Number(2);

	    expect(n instanceof Observable).toBeTruthy();
	});

	it('should have a value', function(){
	    expect(number.value()).toBe(testValue)
	});

	it('should start with an empty list of divisors', function(){
	    expect(number.divisors().length).toBe(0);
	});

	it('should add divisors', function(){
	    number.addDivisor(2);

	    expect(number.divisors()).toContain(2);

	    number.addDivisor(3);

	    expect(number.divisors()).toContain(2);
	    expect(number.divisors()).toContain(3);
	});

	it('should notify when a divisor is added', function(){
	    var isCalled = false;
	    number.addObserver(function(){ isCalled = true; });

	    number.addDivisor(2);

	    expect(isCalled).toBeTruthy();
	});
    });

    describe('Model', function(){
	var n = 20;
	var model;

	beforeEach(function(){
	    model = new Sieve.Model(n);
	});

	it('should be defined', function(){
	    expect(Sieve.Model).toBeDefined();
	});

 	it('should be observable', function(){
	    var m = new Sieve.Model(10);

	    expect(m instanceof Observable).toBeTruthy();
	});

	it('should contain numbers', function(){
	    expect(model.numbers().length).toBe(n);
	});

	it('number on should contain the special divisor \'0\'', function(){
	    expect(model.numbers()[0].divisors()).toContain(0);
	});

	it('should start out not finished', function(){
	    expect(model.finished()).toBeFalsy();
	});

	it('sieveStep should mark multiples of first unrecognized prime', function(){
	    model.sieveStep();

	    var numbers = model.numbers();
	    [4, 6, 8, 10, 12, 14, 16, 18, 20].forEach(function(index){
		expect(numbers[index - 1].divisors()).toContain(2);
	    });
	});

	it('consequtive sieveStep should mark multiples of consequtive primes', function(){
	    model.sieveStep();
	    model.sieveStep();
	    model.sieveStep();

	    var numbers = model.numbers();
	    [4, 6, 8, 10, 12, 14, 16, 18, 20].forEach(function(index){
		expect(numbers[index - 1].divisors()).toContain(2);
	    });
	    [6, 9, 12, 15, 18].forEach(function(index){
		expect(numbers[index - 1].divisors()).toContain(3);
	    });
	    [10, 15, 20].forEach(function(index){
		expect(numbers[index - 1].divisors()).toContain(5);
	    });
	});

	it('should be finished if no candidates can have divisors', function(){
	    expect(model.finished()).toBeFalsy();
	    model.sieveStep();

	    expect(model.finished()).toBeFalsy();
	    model.sieveStep();

	    expect(model.finished()).toBeFalsy();
	    model.sieveStep();

	    expect(model.finished()).toBeFalsy();
	    model.sieveStep();

	    expect(model.finished()).toBeTruthy();
	});

	it('sieve should sieveStep until finished', function(){
	    model.sieve();
	    var numbers = model.numbers();
	    [
		{ divisor: 2, multiples: [4, 6, 8, 10, 12, 14, 16, 18, 20] },
		{ divisor: 3, multiples: [6, 9, 12, 15] },
		{ divisor: 5, multiples: [10, 15] },
		{ divisor: 7, multiples: [14] },
	    ].forEach(function(entry) {
		entry.multiples.forEach(function(index) {
		    expect(numbers[index - 1].divisors()).toContain(entry.divisor);
		});
	    });

	    [2, 3, 5, 7, 11, 13, 17, 19].forEach(function(prime) {
		expect(numbers[prime - 1].divisors().length).toBe(0);
	    });
n	});
    });

    describe('Views', function(){
	beforeEach(function(){
	    var container = document.createElement('div');
	    container.setAttribute('id', 'test-sieve-container');
	    var body = document.getElementsByTagName('body')[0];
	    body.appendChild(container);
	});

	describe('Setup', function(){
	    it('should have created a test container', function(){
		var testContainer = document.getElementById('test-sieve-container');

		expect(testContainer).toBeDefined();
	    });
	});

	describe('Number', function(){
	    var parent;

	    beforeEach(function(){
		parent = document.createElement('span');
		document.getElementById('test-sieve-container').appendChild(parent);
	    });

	    it('should be defined', function(){
		expect(Sieve.NumberView).toBeDefined();
	    });

	    it('should represent the model', function(){
		var n = new Sieve.Number(4);

		new Sieve.NumberView(parent, n);

		expect(parent.textContent).toBe('4');
	    });

	    it('should strike through if model has divisors', function(){
		var n = new Sieve.Number(4);
		n.addDivisor(2);

		new Sieve.NumberView(parent, n);

		expect(parent.style['text-decoration']).toBe('line-through');
	    });

	    it('should strike through when the model acquires divisors', function(){
		var n = new Sieve.Number(4);
		new Sieve.NumberView(parent, n);
		expect(parent.style['text-decoration']).toBe('none');

		n.addDivisor(2);

		expect(parent.style['text-decoration']).toBe('line-through');
	    });
	});

	describe('View', function(){
	    var n = 5;
	    var parent;
	    var model;

	    beforeEach(function(){
		parent = document.createElement('span');
		parent.textContent = 'original';
		document.getElementById('test-sieve-container').appendChild(parent);
	    });

	    beforeEach(function(){
		model = new Sieve.Model(n);
	    });

	    it('should be defined', function(){
		expect(Sieve.View).toBeDefined();
	    });

 	    it('should represent the model', function(){
		new Sieve.View(parent, model);

		expect(parent.children.length).toBe(n);
	    });

	    it('should represent the numbers', function(){
		new Sieve.View(parent, model);

		[1, 2, 3, 4, 5].forEach(function(p){
		    expect(parent.children[p-1].textContent).toBe('' + p);
		});
	    });

	    it('should mark the current index', function(){
		var view = new Sieve.View(parent, model);
		var currentIndex = model.currentIndex;

		view.children().forEach(function(container, index) {
		    var classAttribute = container.getAttribute('class');
		    if (index === currentIndex) {
			expect(classAttribute).toBe('current');
		    } else {
			expect(classAttribute).toBe('');
		    }
		});
	    });

	    it('should update the current index after sieveStep', function(){
		var view = new Sieve.View(parent, model);

		model.sieveStep();
		var currentIndex = model.currentIndex;

		view.children().forEach(function(container, index) {
		    var classAttribute = container.getAttribute('class');
		    if (index === currentIndex) {
			expect(classAttribute).toBe('current');
		    } else {
			expect(classAttribute).toBe('');
		    }
		});
	    });

	    it('should clear the content', function(){
		var view = new Sieve.View(parent, model);

		expect(parent.textContent).not.toContain('original');
	    });
	});

	describe('ControlView', function(){
	    var n = 7;
	    var parent;
	    var model;

	    beforeEach(function(){
		parent = document.createElement('span');
		parent.textContent = 'original';
		document.getElementById('test-sieve-container').appendChild(parent);
	    });

	    beforeEach(function(){
		model = new Sieve.Model(n);
	    });

	    it('should be defined', function(){
		expect(Sieve.ControlView).toBeDefined();
	    });

 	    it('should have controls and container', function(){
		new Sieve.ControlView(parent, model);

		expect(parent.children.length).toBe(3);
	    });

	    it('should represent the numbers', function(){
		new Sieve.ControlView(parent, model);

		[1, 2, 3, 4, 5].forEach(function(p){
		    expect(parent.children[2].children[p-1].textContent).toBe('' + p);
		});
	    });

	    it('should represent the controls', function(){
		new Sieve.ControlView(parent, model);

		expect(parent.children[0].textContent).toBe('>');
		expect(parent.children[1].textContent).toBe('>>');
	    });

	    it('should clear the content', function(){
		var view = new Sieve.ControlView(parent, model);

		expect(parent.textContent).not.toContain('original');
	    });
	});

	afterEach(function(){
	    var container = document.getElementById('test-sieve-container');
	    container.parentNode.removeChild(container);
	});
    });

});
<|endoftext|>


<commit_before>'use strict';

// NOTE run ./node_modules/mocha/bin/mocha ./spec/validation.js

const fs = require('fs');
const path = require('path');
const CSON = require('cson');
const expect = require('chai').expect;

const LOCALES = [
  'ar',
  'es',
  'de',
  'fr',
  'hi',
  'ja',
  'ko',
  'nl',
  'pt-br',
  'zh-cn',
  'zh-tw',
  'template'
];
const FILES = [
  'menu_darwin.cson', 'menu_linux.cson', 'menu_win32.cson',
  'context.cson', 'settings.cson', 'about.cson'
];

describe('validation', () => {

  let packageMeta = {};

  describe('package.json validation', () => {

    it('loads package.json', () => {
      let loading = () => {
        packageMeta = JSON.parse(fs.readFileSync('./package.json'), 'utf8');
      };
      expect(loading).not.to.throw(Error);
    });

    it('checks locale options list', () => {
      let locales = packageMeta.configSchema.locale.enum.map((opt)=>{return opt.value;});
      expect(locales).to.deep.equal(LOCALES);
    });
  });

  describe('cson file validation', () => {

    describe('reading cson files of each locale', () => {
      for (let locale of LOCALES) {
        for (let file of FILES) {
          it(`reads "${path.join(locale, file)}"`, () => {
            let content = CSON.load(path.join(__dirname, '../def', locale, file));
            expect(content).not.to.be.instanceof(Error);
          });
        }
      }
    });

    describe('checking each cson files of all locales should have same number of key-value pairs', () => {

      JSON.flatten = function(data) {
        var result = {};
        function recurse (cur, prop) {
          if (Object(cur) !== cur) {
            result[prop] = cur;
          } else if (Array.isArray(cur)) {
            for(var i=0, l=cur.length; i<l; i++)
              recurse(cur[i], prop + '[' + i + ']');
            if (l == 0) {
              result[prop] = [];
            }
          } else {
            var isEmpty = true;
            for (var p in cur) {
              isEmpty = false;
              recurse(cur[p], prop ? prop+'.'+p : p);
            }
            if (isEmpty && prop) {
              result[prop] = {};
            }
          }
        }
        recurse(data, '');
        return result;
      };

      function countKeysFromCSON(_path) {
        return Object.keys(JSON.flatten(CSON.load(_path))).length;
      }

      let stat = {};
      for (let file of FILES) {
        stat[file] = [];
        for (let locale of LOCALES) {
          let keyCount = countKeysFromCSON(path.join(__dirname, '../def', locale, file));
          stat[file].push(keyCount);
        }
        let maxCount = Math.max.apply(null, stat[file]);
        let checkRes = Boolean((new Set(stat[file])).size === 1);
        console.log(`*/${file}: ${(checkRes)?'ok':'Err: some less than ' + maxCount}`);
      }

      for (let file of FILES) {
        for (let locale of LOCALES) {
          it(`checks "${path.join(locale, file)}"`, () => {
            let keyCount = countKeysFromCSON(path.join(__dirname, '../def', locale, file));
            expect(keyCount).to.equal(Math.max.apply(null, stat[file]));
          });
        }
      }
    });

  });

});
<commit_msg>refactor cson validation<commit_after>'use strict';

// NOTE run ./node_modules/mocha/bin/mocha ./spec/validation.js

const fs = require('fs');
const path = require('path');
const CSON = require('cson');
const expect = require('chai').expect;

const LOCALES = [
  'ar',
  'es',
  'de',
  'fr',
  'hi',
  'ja',
  'ko',
  'nl',
  'pt-br',
  'zh-cn',
  'zh-tw',
  'template'
];
const CsonFiles = [
  'menu_darwin.cson', 'menu_linux.cson', 'menu_win32.cson',
  'context.cson', 'settings.cson', 'about.cson'
];

describe('validation', () => {

  let packageMeta = {};

  describe('package.json validation', () => {

    it('loads package.json', () => {
      let loading = () => {
        packageMeta = JSON.parse(fs.readFileSync('./package.json'), 'utf8');
      };
      expect(loading).not.to.throw(Error);
    });

    it('checks locale options list', () => {
      let locales = packageMeta.configSchema.locale.enum.map((opt)=>{return opt.value;});
      expect(locales).to.deep.equal(LOCALES);
    });
  });

  describe('cson file validation', () => {

    describe('checking each cson files of all locales having same key-value pairs as template/*.cson', () => {

      JSON.flatten = function(data) {
        var result = {};
        function recurse (cur, prop) {
          if (Object(cur) !== cur) {
            result[prop] = cur;
          } else if (Array.isArray(cur)) {
            for(var i=0, l=cur.length; i<l; i++)
              recurse(cur[i], prop + '[' + i + ']');
            if (l == 0) {
              result[prop] = [];
            }
          } else {
            var isEmpty = true;
            for (var p in cur) {
              isEmpty = false;
              recurse(cur[p], prop ? prop+'.'+p : p);
            }
            if (isEmpty && prop) {
              result[prop] = {};
            }
          }
        }
        recurse(data, '');
        return result;
      };

      let templateKeys = {};
      for (let csonFile of CsonFiles) {
        templateKeys[csonFile] = Object.keys(JSON.flatten(CSON.load(path.join(__dirname, '../def/template', csonFile))));
      }

      for (let locale of LOCALES) {
        describe(`checking locale ${locale}`, () => {
          for (let csonFile of CsonFiles) {
            it(`checks "${path.join(locale, csonFile)}"`, () => {
              let cson = CSON.load(path.join(__dirname, '../def', locale, csonFile));
              expect(cson).not.to.be.instanceof(Error);

              let localeCsonKeys = Object.keys(JSON.flatten(cson));
              expect(localeCsonKeys).to.deep.equal(templateKeys[csonFile]);
            });
          }
        });
      }
    });

  });

});
<|endoftext|>


<commit_before>GuestbookEntries = new Mongo.Collection('guestbook_entries');

var formSchema = {
	text: {
		type: String,
		label: "Review text",
		max: 256
	}
};
GuestbookEntriesFormSchema = new SimpleSchema(formSchema);

GuestbookEntriesSchema = new SimpleSchema(Object.assign(formSchema, {
	author: {
		type: String,
		label: "Author",
		max: 100
	},
	number: {
		type: Number,
		label: "Number",
		max: 1000
	},
	answer: {
		type: String,
		label: "Answer",
		optional: true
	},
	date: {
		type: Date,
		label: "Date"
	},
	approved: {
		type: Boolean,
		label: "Approved"
	}
}));
GuestbookEntries.attachSchema(GuestbookEntriesSchema);

if (Meteor.isServer) {
	GuestbookEntries.allow({
		insert: function (userId, doc) {
			return false;
		},

		update: function (userId, doc, fieldNames, modifier) {
			return false;
		},

		remove: function (userId, doc) {
			return false;
		}
	});

	GuestbookEntries.deny({
		insert: function (userId, doc) {
			return true;
		},

		update: function (userId, doc, fieldNames, modifier) {
			return true;
		},

		remove: function (userId, doc) {
			return true;
		}
	});
}
<commit_msg>Fix indent.<commit_after>GuestbookEntries = new Mongo.Collection('guestbook_entries');

var formSchema = {
  text: {
    type: String,
    label: "Review text",
    max: 256
  }
};
GuestbookEntriesFormSchema = new SimpleSchema(formSchema);

GuestbookEntriesSchema = new SimpleSchema(Object.assign(formSchema, {
  author: {
    type: String,
    label: "Author",
    max: 100
  },
  number: {
    type: Number,
    label: "Number",
    max: 1000
  },
  answer: {
    type: String,
    label: "Answer",
    optional: true
  },
  date: {
    type: Date,
    label: "Date"
  },
  approved: {
    type: Boolean,
    label: "Approved"
  }
}));
GuestbookEntries.attachSchema(GuestbookEntriesSchema);

if (Meteor.isServer) {
  GuestbookEntries.allow({
    insert: function (userId, doc) {
      return false;
    },

    update: function (userId, doc, fieldNames, modifier) {
      return false;
    },

    remove: function (userId, doc) {
      return false;
    }
  });

  GuestbookEntries.deny({
    insert: function (userId, doc) {
      return true;
    },

    update: function (userId, doc, fieldNames, modifier) {
      return true;
    },

    remove: function (userId, doc) {
      return true;
    }
  });
}
<|endoftext|>