\r\nGiven two strings s and t which consist of only lowercase letters.
\r\n\r\nString t is generated by random shuffling string s and then add one more letter at a random position.
\r\n\r\nFind the letter that was added in t.
\r\n\r\nExample:\r\n
\r\nInput:\r\ns = \"abcd\"\r\nt = \"abcde\"\r\n\r\nOutput:\r\ne\r\n\r\nExplanation:\r\n'e' is the letter that was added.\r\n","stats":"{\"totalAccepted\": \"89.7K\", \"totalSubmission\": \"175.7K\"}","codeDefinition":"[{\"text\": \"C++\", \"value\": \"cpp\", \"defaultCode\": \"class Solution {\\r\\npublic:\\r\\n char findTheDifference(string s, string t) {\\r\\n \\r\\n }\\r\\n};\"}, {\"text\": \"Java\", \"value\": \"java\", \"defaultCode\": \"class Solution {\\r\\n public char findTheDifference(String s, String t) {\\r\\n \\r\\n }\\r\\n}\"}, {\"text\": \"Python\", \"value\": \"python\", \"defaultCode\": \"class Solution(object):\\r\\n def findTheDifference(self, s, t):\\r\\n \\\"\\\"\\\"\\r\\n :type s: str\\r\\n :type t: str\\r\\n :rtype: str\\r\\n \\\"\\\"\\\"\\r\\n \"}, {\"text\": \"Python3\", \"value\": \"python3\", \"defaultCode\": \"class Solution:\\r\\n def findTheDifference(self, s, t):\\r\\n \\\"\\\"\\\"\\r\\n :type s: str\\r\\n :type t: str\\r\\n :rtype: str\\r\\n \\\"\\\"\\\"\\r\\n \"}, {\"text\": \"C\", \"value\": \"c\", \"defaultCode\": \"char findTheDifference(char* s, char* t) {\\r\\n \\r\\n}\"}, {\"text\": \"C#\", \"value\": \"csharp\", \"defaultCode\": \"public class Solution {\\r\\n public char FindTheDifference(string s, string t) {\\r\\n \\r\\n }\\r\\n}\"}, {\"text\": \"JavaScript\", \"value\": \"javascript\", \"defaultCode\": \"/**\\r\\n * @param {string} s\\r\\n * @param {string} t\\r\\n * @return {character}\\r\\n */\\r\\nvar findTheDifference = function(s, t) {\\r\\n \\r\\n};\"}, {\"text\": \"Ruby\", \"value\": \"ruby\", \"defaultCode\": \"# @param {String} s\\r\\n# @param {String} t\\r\\n# @return {Character}\\r\\ndef find_the_difference(s, t)\\r\\n \\r\\nend\"}, {\"text\": \"Swift\", \"value\": \"swift\", \"defaultCode\": \"class Solution {\\r\\n func findTheDifference(_ s: String, _ t: String) -> Character {\\r\\n \\r\\n }\\r\\n}\"}, {\"text\": \"Go\", \"value\": \"golang\", \"defaultCode\": \"func findTheDifference(s string, t string) byte {\\r\\n \\r\\n}\"}, {\"text\": \"Scala\", \"value\": \"scala\", \"defaultCode\": \"object Solution {\\n def findTheDifference(s: String, t: String): Char = {\\n \\n }\\n}\"}, {\"text\": \"Kotlin\", \"value\": \"kotlin\", \"defaultCode\": \"class Solution {\\n fun findTheDifference(s: String, t: String): Char {\\n \\n }\\n}\"}]","sampleTestCase":"\"abcd\"\n\"abcde\"","enableRunCode":true,"metaData":"{\r\n \"name\": \"findTheDifference\",\r\n \"params\": [\r\n {\r\n \"name\": \"s\",\r\n \"type\": \"string\"\r\n },\r\n {\r\n \"name\": \"t\",\r\n \"type\": \"string\"\r\n }\r\n ],\r\n \"return\": {\r\n \"type\": \"character\"\r\n }\r\n}","discussCategoryId":"511"}}} \ No newline at end of file diff --git a/test/plugins/test_cache.js b/test/plugins/test_cache.js index 68f881ba..6b3114ca 100644 --- a/test/plugins/test_cache.js +++ b/test/plugins/test_cache.js @@ -1,48 +1,51 @@ -var execSync = require('child_process').execSync; -var fs = require('fs'); +'use strict'; +const _ = require('underscore'); +const assert = require('chai').assert; +const rewire = require('rewire'); -var _ = require('underscore'); -var assert = require('chai').assert; -var rewire = require('rewire'); - -var log = require('../../lib/log'); -var config = require('../../lib/config'); - -var cache = rewire('../../lib/cache'); -var h = rewire('../../lib/helper'); -var session = rewire('../../lib/session'); -var plugin = rewire('../../lib/plugins/cache'); - -var HOME = './tmp'; +const h = require('../../lib/helper'); +const log = require('../../lib/log'); +const config = require('../../lib/config'); +const th = require('../helper'); describe('plugin:cache', function() { - var PROBLEMS = [ - {id: 0, name: 'name0', slug: 'slug0', starred: false, category: 'algorithms'}, - {id: 1, name: 'name1', slug: 'slug1', starred: true, category: 'algorithms'} + let plugin; + let next; + let cache; + let file; + let session; + + const PROBLEMS = [ + {id: 0, fid: 0, name: 'name0', slug: 'slug0', starred: false, category: 'algorithms'}, + {id: 1, fid: 1, name: 'name1', slug: 'slug1', starred: true, category: 'algorithms'} ]; - var PROBLEM = {id: 0, slug: 'slug0', category: 'algorithms'}; - - var NEXT = {}; + const PROBLEM = {id: 0, fid: 0, slug: 'slug0', category: 'algorithms'}; before(function() { log.init(); config.init(); - plugin.init(); + }); + + beforeEach(function() { + th.clean(); + next = {}; + + file = rewire('../../lib/file'); + file.cacheDir = () => th.DIR; - h.getHomeDir = function() { - return HOME; - }; + cache = rewire('../../lib/cache'); + cache.__set__('file', file); + cache.init(); - cache.__set__('h', h); + session = rewire('../../lib/session'); session.__set__('cache', cache); + + plugin = rewire('../../lib/plugins/cache'); plugin.__set__('cache', cache); plugin.__set__('session', session); - plugin.setNext(NEXT); - }); + plugin.init(); - beforeEach(function() { - execSync('rm -rf ' + HOME); - fs.mkdirSync(HOME); + plugin.setNext(next); }); describe('#getProblems', function() { @@ -58,10 +61,7 @@ describe('plugin:cache', function() { it('should getProblems w/o cache ok', function(done) { cache.del('problems'); - - NEXT.getProblems = function(cb) { - return cb(null, PROBLEMS); - }; + next.getProblems = cb => cb(null, PROBLEMS); plugin.getProblems(function(e, problems) { assert.equal(e, null); @@ -72,10 +72,7 @@ describe('plugin:cache', function() { it('should getProblems w/o cache fail if client error', function(done) { cache.del('problems'); - - NEXT.getProblems = function(cb) { - return cb('client getProblems error'); - }; + next.getProblems = cb => cb('client getProblems error'); plugin.getProblems(function(e, problems) { assert.equal(e, 'client getProblems error'); @@ -99,10 +96,7 @@ describe('plugin:cache', function() { it('should getProblem w/o cache ok', function(done) { cache.set('problems', PROBLEMS); cache.del('0.slug0.algorithms'); - - NEXT.getProblem = function(problem, cb) { - return cb(null, PROBLEMS[0]); - }; + next.getProblem = (problem, cb) => cb(null, PROBLEMS[0]); plugin.getProblem(_.clone(PROBLEM), function(e, problem) { assert.equal(e, null); @@ -114,10 +108,7 @@ describe('plugin:cache', function() { it('should getProblem fail if client error', function(done) { cache.set('problems', PROBLEMS); cache.del('0.slug0.algorithms'); - - NEXT.getProblem = function(problem, cb) { - return cb('client getProblem error'); - }; + next.getProblem = (problem, cb) => cb('client getProblem error'); plugin.getProblem(_.clone(PROBLEM), function(e, problem) { assert.equal(e, 'client getProblem error'); @@ -130,14 +121,14 @@ describe('plugin:cache', function() { it('should ok', function() { cache.del('0.slug0.algorithms'); - var problem = _.clone(PROBLEMS[0]); + const problem = _.clone(PROBLEMS[0]); problem.locked = true; problem.state = 'ac'; - var ret = plugin.saveProblem(problem); + const ret = plugin.saveProblem(problem); assert.equal(ret, true); assert.deepEqual(cache.get('0.slug0.algorithms'), - {id: 0, slug: 'slug0', name: 'name0', category: 'algorithms'}); + {id: 0, fid: 0, slug: 'slug0', name: 'name0', category: 'algorithms'}); }); }); // #saveProblem @@ -145,15 +136,15 @@ describe('plugin:cache', function() { it('should updateProblem ok', function(done) { cache.set('problems', PROBLEMS); - var kv = {value: 'value00'}; - var ret = plugin.updateProblem(PROBLEMS[0], kv); + const kv = {value: 'value00'}; + const ret = plugin.updateProblem(PROBLEMS[0], kv); assert.equal(ret, true); plugin.getProblems(function(e, problems) { assert.equal(e, null); assert.deepEqual(problems, [ - {id: 0, name: 'name0', slug: 'slug0', value: 'value00', starred: false, category: 'algorithms'}, - {id: 1, name: 'name1', slug: 'slug1', starred: true, category: 'algorithms'} + {id: 0, fid: 0, name: 'name0', slug: 'slug0', value: 'value00', starred: false, category: 'algorithms'}, + {id: 1, fid: 1, name: 'name1', slug: 'slug1', starred: true, category: 'algorithms'} ]); done(); }); @@ -161,31 +152,29 @@ describe('plugin:cache', function() { it('should updateProblem fail if no problems found', function() { cache.del('problems'); - var ret = plugin.updateProblem(PROBLEMS[0], {}); + const ret = plugin.updateProblem(PROBLEMS[0], {}); assert.equal(ret, false); }); it('should updateProblem fail if unknown problem', function() { cache.set('problems', [PROBLEMS[1]]); - var ret = plugin.updateProblem(PROBLEMS[0], {}); + const ret = plugin.updateProblem(PROBLEMS[0], {}); assert.equal(ret, false); }); }); // #updateProblem describe('#user', function() { - var USER = {name: 'test-user', pass: 'password'}; - var USER_SAFE = {name: 'test-user'}; + const USER = {name: 'test-user', pass: 'password'}; + const USER_SAFE = {name: 'test-user'}; it('should login ok', function(done) { - config.AUTO_LOGIN = true; + config.autologin.enable = true; // before login - cache.del('.user'); + cache.del(h.KEYS.user); assert.equal(session.getUser(), null); assert.equal(session.isLogin(), false); - NEXT.login = function(user, cb) { - return cb(null, user); - }; + next.login = (user, cb) => cb(null, user); plugin.login(USER, function(e, user) { assert.equal(e, null); @@ -199,12 +188,10 @@ describe('plugin:cache', function() { }); it('should login ok w/ auto login', function(done) { - config.AUTO_LOGIN = false; - cache.del('.user'); + config.autologin.enable = false; + cache.del(h.KEYS.user); - NEXT.login = function(user, cb) { - return cb(null, user); - }; + next.login = (user, cb) => cb(null, user); plugin.login(USER, function(e, user) { assert.equal(e, null); @@ -216,9 +203,7 @@ describe('plugin:cache', function() { }); it('should login fail if client login error', function(done) { - NEXT.login = function(user, cb) { - return cb('client login error'); - }; + next.login = (user, cb) => cb('client login error'); plugin.login(USER, function(e, user) { assert.equal(e, 'client login error'); @@ -228,7 +213,7 @@ describe('plugin:cache', function() { it('should logout ok', function(done) { // before logout - cache.set('.user', USER); + cache.set(h.KEYS.user, USER); assert.deepEqual(session.getUser(), USER); assert.equal(session.isLogin(), true); @@ -238,5 +223,18 @@ describe('plugin:cache', function() { assert.equal(session.isLogin(), false); done(); }); + + it('should logout ok', function(done) { + // before logout + cache.set(h.KEYS.user, USER); + assert.deepEqual(session.getUser(), USER); + assert.equal(session.isLogin(), true); + + // after logout + plugin.logout(null, true); + assert.equal(session.getUser(), null); + assert.equal(session.isLogin(), false); + done(); + }); }); // #user }); diff --git a/test/plugins/test_leetcode.js b/test/plugins/test_leetcode.js index ce29ccab..ef099b9b 100644 --- a/test/plugins/test_leetcode.js +++ b/test/plugins/test_leetcode.js @@ -1,17 +1,19 @@ -var _ = require('underscore'); -var assert = require('chai').assert; -var nock = require('nock'); -var rewire = require('rewire'); +'use strict'; +const _ = require('underscore'); +const assert = require('chai').assert; +const nock = require('nock'); +const rewire = require('rewire'); -var config = require('../../lib/config'); -var log = require('../../lib/log'); +const config = require('../../lib/config'); +const chalk = require('../../lib/chalk'); +const log = require('../../lib/log'); -var plugin = rewire('../../lib/plugins/leetcode'); -var session = rewire('../../lib/session'); +const plugin = rewire('../../lib/plugins/leetcode'); +const session = rewire('../../lib/session'); describe('plugin:leetcode', function() { - var USER = {hash: 'abcdef'}; - var PROBLEM = { + const USER = {hash: 'abcdef'}; + const PROBLEM = { id: 389, name: 'Find the Difference', slug: 'find-the-difference', @@ -19,7 +21,7 @@ describe('plugin:leetcode', function() { locked: false, file: '/dev/null' }; - var SUBMISSION = { + const SUBMISSION = { id: '73790064', lang: 'cpp', runtime: '9 ms', @@ -30,12 +32,11 @@ describe('plugin:leetcode', function() { before(function() { log.init(); config.init(); + chalk.init(); plugin.init(); - session.getUser = function() { - return USER; - }; - session.saveUser = function() {}; + session.getUser = () => USER; + session.saveUser = () => {}; plugin.__set__('session', session); }); @@ -43,8 +44,7 @@ describe('plugin:leetcode', function() { it('should ok', function(done) { nock('https://leetcode.com') .get('/accounts/login/') - .reply(200, '', { - 'Set-Cookie': [ + .reply(200, '', { 'Set-Cookie': [ 'csrftoken=LOGIN_CSRF_TOKEN; Max-Age=31449600; Path=/; secure' ]}); @@ -53,13 +53,17 @@ describe('plugin:leetcode', function() { .reply(302, '', { 'Set-Cookie': [ 'csrftoken=SESSION_CSRF_TOKEN; Max-Age=31449600; Path=/; secure', - 'LEETCODE_SESSION=SESSION_ID; Max-Age=31449600; Path=/; secure', - "messages='Successfully signed in as Eric.'; Max-Age=31449600; Path=/; secure" + 'LEETCODE_SESSION=SESSION_ID; Max-Age=31449600; Path=/; secure' ]}); nock('https://leetcode.com') .get('/list/api/questions') - .reply(200, JSON.stringify({favorites: {private_favorites: [{id_hash: 'abcdef', name: 'Favorite'}]}})); + .reply(200, JSON.stringify({ + user_name: 'Eric', + favorites: { + private_favorites: [{id_hash: 'abcdef', name: 'Favorite'}] + } + })); plugin.login({}, function(e, user) { assert.equal(e, null); @@ -158,7 +162,7 @@ describe('plugin:leetcode', function() { }); it('should fail if not login', function(done) { - config.AUTO_LOGIN = false; + config.autologin.enable = false; nock('https://leetcode.com') .get('/api/problems/algorithms/') .replyWithFile(200, './test/mock/problems.nologin.json.20161015'); @@ -171,15 +175,19 @@ describe('plugin:leetcode', function() { }); // #getCategoryProblems describe('#getProblem', function() { + beforeEach(function() { + PROBLEM.locked = false; + }); + it('should ok', function(done) { nock('https://leetcode.com') - .get('/problems/find-the-difference') - .replyWithFile(200, './test/mock/find-the-difference.html.20170714'); + .post('/graphql') + .replyWithFile(200, './test/mock/find-the-difference.json.20171216'); plugin.getProblem(PROBLEM, function(e, problem) { assert.equal(e, null); - assert.equal(problem.totalAC, '73.2K'); - assert.equal(problem.totalSubmit, '142K'); + assert.equal(problem.totalAC, '89.7K'); + assert.equal(problem.totalSubmit, '175.7K'); assert.equal(problem.desc, [ '', @@ -203,7 +211,7 @@ describe('plugin:leetcode', function() { '' ].join('\r\n')); - assert.equal(problem.templates.length, 11); + assert.equal(problem.templates.length, 12); assert.equal(problem.templates[0].value, 'cpp'); assert.equal(problem.templates[0].text, 'C++'); @@ -221,7 +229,7 @@ describe('plugin:leetcode', function() { assert.equal(problem.templates[1].text, 'Java'); assert.equal(problem.templates[1].defaultCode, [ - 'public class Solution {', + 'class Solution {', ' public char findTheDifference(String s, String t) {', ' ', ' }', @@ -333,15 +341,23 @@ describe('plugin:leetcode', function() { '}' ].join('\n')); + assert.equal(problem.templates[11].value, 'kotlin'); + assert.equal(problem.templates[11].text, 'Kotlin'); + assert.equal(problem.templates[11].defaultCode, + [ + 'class Solution {', + ' fun findTheDifference(s: String, t: String): Char {', + ' ', + ' }', + '}' + ].join('\n')); + done(); }); }); it('should fail if no permission for locked', function(done) { PROBLEM.locked = true; - nock('https://leetcode.com') - .get('/problems/find-the-difference') - .replyWithFile(200, './test/mock/locked.html.20161015'); plugin.getProblem(PROBLEM, function(e, problem) { assert.equal(e, 'failed to load locked problem!'); @@ -350,9 +366,7 @@ describe('plugin:leetcode', function() { }); it('should fail if session expired', function(done) { - nock('https://leetcode.com') - .get('/problems/find-the-difference') - .reply(403); + nock('https://leetcode.com').post('/graphql').reply(403); plugin.getProblem(PROBLEM, function(e, problem) { assert.equal(e, session.errors.EXPIRED); @@ -361,9 +375,7 @@ describe('plugin:leetcode', function() { }); it('should fail if http error', function(done) { - nock('https://leetcode.com') - .get('/problems/find-the-difference') - .reply(500); + nock('https://leetcode.com').post('/graphql').reply(500); plugin.getProblem(PROBLEM, function(e, problem) { assert.deepEqual(e, {msg: 'http error', statusCode: 500}); @@ -372,9 +384,7 @@ describe('plugin:leetcode', function() { }); it('should fail if unknown error', function(done) { - nock('https://leetcode.com') - .get('/problems/find-the-difference') - .replyWithError('unknown error!'); + nock('https://leetcode.com').post('/graphql').replyWithError('unknown error!'); plugin.getProblem(PROBLEM, function(e, problem) { assert.equal(e.message, 'unknown error!'); @@ -438,8 +448,6 @@ describe('plugin:leetcode', function() { }); it('should ok after delay', function(done) { - this.timeout(5000); - nock('https://leetcode.com') .post('/problems/find-the-difference/submit/') .reply(200, '{"error": "You run code too soon"}'); @@ -460,7 +468,7 @@ describe('plugin:leetcode', function() { assert.equal(results[0].ok, true); done(); }); - }); + }).timeout(5000); it('should fail if server error', function(done) { nock('https://leetcode.com') @@ -528,7 +536,7 @@ describe('plugin:leetcode', function() { describe('#getSubmissions', function() { it('should ok', function(done) { - var problem = { + const problem = { id: 1, name: 'Two Sum', slug: 'two-sum', @@ -636,7 +644,7 @@ describe('plugin:leetcode', function() { plugin.getFavorites(function(e, favorites) { assert.equal(e, null); - var my = favorites.favorites.private_favorites; + const my = favorites.favorites.private_favorites; assert.equal(my.length, 1); assert.equal(my[0].name, 'Favorite'); assert.equal(my[0].id_hash, 'abcdefg'); @@ -644,4 +652,68 @@ describe('plugin:leetcode', function() { }); }); }); // #getFavorites + + describe('#session', function() { + const DATA = {sessions: []}; + + it('should getSessions ok', function(done) { + nock('https://leetcode.com') + .post('/session/') + .reply(200, JSON.stringify(DATA)); + + plugin.getSessions(function(e, sessions) { + assert.notExists(e); + assert.deepEqual(sessions, []); + done(); + }); + }); + + it('should activateSessions ok', function(done) { + nock('https://leetcode.com') + .put('/session/', {func: 'activate', target: 1}) + .reply(200, JSON.stringify(DATA)); + + plugin.activateSession({id: 1}, function(e, sessions) { + assert.notExists(e); + assert.deepEqual(sessions, []); + done(); + }); + }); + + it('should createSessions ok', function(done) { + nock('https://leetcode.com') + .put('/session/', {func: 'create', name: 's1'}) + .reply(200, JSON.stringify(DATA)); + + plugin.createSession('s1', function(e, sessions) { + assert.notExists(e); + assert.deepEqual(sessions, []); + done(); + }); + }); + + it('should deleteSessions ok', function(done) { + nock('https://leetcode.com') + .delete('/session/', {target: 1}) + .reply(200, JSON.stringify(DATA)); + + plugin.deleteSession({id: 1}, function(e, sessions) { + assert.notExists(e); + assert.deepEqual(sessions, []); + done(); + }); + }); + + it('should fail if 302 returned', function(done) { + nock('https://leetcode.com') + .post('/session/') + .reply(302); + + plugin.getSessions(function(e, sessions) { + assert.deepEqual(e, session.errors.EXPIRED); + assert.notExists(sessions); + done(); + }); + }); + }); // #session }); diff --git a/test/plugins/test_retry.js b/test/plugins/test_retry.js index 4a249ada..dbdb060c 100644 --- a/test/plugins/test_retry.js +++ b/test/plugins/test_retry.js @@ -1,25 +1,24 @@ -var assert = require('chai').assert; -var rewire = require('rewire'); +'use strict'; +const assert = require('chai').assert; +const rewire = require('rewire'); -var log = require('../../lib/log'); +const log = require('../../lib/log'); -var config = rewire('../../lib/config'); -var session = rewire('../../lib/session'); -var plugin = rewire('../../lib/plugins/retry'); +const config = rewire('../../lib/config'); +const session = rewire('../../lib/session'); +const plugin = rewire('../../lib/plugins/retry'); describe('plugin:retry', function() { - var USER = {}; - var NEXT = {}; - var PROBLEMS = [{id: 0, name: 'name0'}]; + const USER = {}; + const NEXT = {}; + const PROBLEMS = [{id: 0, name: 'name0'}]; before(function() { log.init(); config.init(); plugin.init(); - session.getUser = function() { - return USER; - }; + session.getUser = () => USER; plugin.__set__('config', config); plugin.__set__('session', session); @@ -27,10 +26,8 @@ describe('plugin:retry', function() { }); it('should fail if auto login disabled', function(done) { - config.AUTO_LOGIN = false; - NEXT.getProblems = function(cb) { - return cb(session.errors.EXPIRED); - }; + config.autologin.enable = false; + NEXT.getProblems = cb => cb(session.errors.EXPIRED); plugin.getProblems(function(e, problems) { assert.equal(e, session.errors.EXPIRED); @@ -38,53 +35,60 @@ describe('plugin:retry', function() { }); }); - it('should retry if session expired', function(done) { - config.AUTO_LOGIN = true; + it('should retry ok if finally ok', function(done) { + config.autologin.enable = true; + config.autologin.retry = 3; - var n = 0; + let n = 0; NEXT.getProblems = function(cb) { - ++n; - if (n === 1) return cb(session.errors.EXPIRED); - return cb(null, PROBLEMS); - }; - - NEXT.login = function(user, cb) { - return cb(null, user); + return ++n <= 3 ? cb(session.errors.EXPIRED) : cb(null, PROBLEMS); }; + NEXT.login = (user, cb) => cb(null, user); plugin.getProblems(function(e, problems) { - assert.equal(e, null); + assert.notExists(e); assert.equal(problems, PROBLEMS); done(); }); }); - it('should fail if user expired locally', function(done) { - config.AUTO_LOGIN = true; + it('should retry fail if always failed', function(done) { + config.autologin.enable = true; + config.autologin.retry = 2; - var n = 0; + let n = 0; NEXT.getProblems = function(cb) { - ++n; - if (n === 1) return cb(session.errors.EXPIRED); - return cb(null, PROBLEMS); + return ++n <= 3 ? cb(session.errors.EXPIRED) : cb(null, PROBLEMS); }; + NEXT.login = (user, cb) => { + return n == 1 ? cb(null, user) : cb('login failed'); + } - session.getUser = function() { - return null; + plugin.getProblems(function(e) { + assert.deepEqual(e, session.errors.EXPIRED); + done(); + }); + }); + + it('should fail if user expired locally', function(done) { + config.autologin.enable = true; + + let n = 0; + NEXT.getProblems = function(cb) { + return ++n === 1 ? cb(session.errors.EXPIRED) : cb(null, PROBLEMS); }; + session.getUser = () => null; plugin.getProblems(function(e, problems) { - assert.equal(e, null); + assert.notExists(e); assert.equal(problems, PROBLEMS); done(); }); }); it('should fail if other errors', function(done) { - config.AUTO_LOGIN = true; - NEXT.getProblems = function(cb) { - return cb('unknown error'); - }; + config.autologin.enable = true; + NEXT.getProblems = cb => cb('unknown error'); plugin.getProblems(function(e, problems) { assert.equal(e, 'unknown error'); diff --git a/test/test_cache.js b/test/test_cache.js index 4e3405f9..caba14c1 100644 --- a/test/test_cache.js +++ b/test/test_cache.js @@ -1,60 +1,48 @@ -var execSync = require('child_process').execSync; +'use strict'; +const assert = require('chai').assert; +const rewire = require('rewire'); -var assert = require('chai').assert; -var rewire = require('rewire'); - -var cache = rewire('../lib/cache'); -var h = rewire('../lib/helper'); +const th = require('./helper'); describe('cache', function() { - var k = '.test'; - var v = {test: 'data'}; + let cache; - before(function() { - var cachedir = './tmp'; - execSync('rm -rf ' + cachedir); + const K = '.test'; + const V = {test: 'data'}; - h.getCacheDir = function() { - return cachedir; - }; - cache.__set__('h', h); - }); + beforeEach(function() { + th.clean(); - it('should ok when not cached', function() { - cache.del(k); + const file = rewire('../lib/file'); + file.cacheDir = () => th.DIR; - assert.equal(cache.get(k), null); - assert.equal(cache.del(k), false); + cache = rewire('../lib/cache'); + cache.__set__('file', file); + cache.init(); }); - it('should ok when cached', function() { - assert.equal(cache.set(k, v), true); + it('should get ok when not cached', function() { + cache.del(K); + assert.equal(cache.get(K), null); + assert.equal(cache.del(K), false); + }); - assert.deepEqual(cache.get(k), v); - assert.equal(cache.del(k), true); + it('should get ok when cached', function() { + assert.equal(cache.set(K, V), true); + assert.deepEqual(cache.get(K), V); + assert.equal(cache.del(K), true); }); it('should list ok when no cached', function() { - var items = cache.list(); + const items = cache.list(); assert.equal(items.length, 0); }); it('should list ok when cached', function() { - assert.equal(cache.set(k, v), true); - - var items = cache.list(); + assert.equal(cache.set(K, V), true); + const items = cache.list(); assert.equal(items.length, 1); - - assert.equal(items[0].name, k); - assert.equal(items[0].size, JSON.stringify(v).length); - }); - - it('should list ok when cache dir not exist', function() { - h.getCacheDir = function() { - return '/not-exist-dir'; - }; - - var items = cache.list(); - assert.equal(items.length, 0); + assert.equal(items[0].name, K); + assert.equal(items[0].size, JSON.stringify(V).length); }); }); diff --git a/test/test_chalk.js b/test/test_chalk.js new file mode 100644 index 00000000..b0c17089 --- /dev/null +++ b/test/test_chalk.js @@ -0,0 +1,91 @@ +'use strict'; +const assert = require('chai').assert; +const rewire = require('rewire'); + +// refer to https://en.wikipedia.org/wiki/ANSI_escape_code +describe('chalk', function() { + let chalk; + + beforeEach(function() { + chalk = rewire('../lib/chalk'); + chalk.enabled = true; + chalk.use256 = true; + chalk.use16m = false; + }); + + it('should ok w/ 256 colors', function() { + chalk.init(); + chalk.setTheme('default'); + + assert.equal(chalk.black(' '), '\u001b[38;5;16m \u001b[39m'); + assert.equal(chalk.red(' '), '\u001b[38;5;196m \u001b[39m'); + assert.equal(chalk.green(' '), '\u001b[38;5;46m \u001b[39m'); + assert.equal(chalk.yellow(' '), '\u001b[38;5;226m \u001b[39m'); + assert.equal(chalk.blue(' '), '\u001b[38;5;21m \u001b[39m'); + assert.equal(chalk.magenta(' '), '\u001b[38;5;201m \u001b[39m'); + assert.equal(chalk.cyan(' '), '\u001b[38;5;51m \u001b[39m'); + assert.equal(chalk.white(' '), '\u001b[38;5;231m \u001b[39m'); + + assert.equal(chalk.bold(' '), '\u001b[1m \u001b[22m'); + assert.equal(chalk.dim(' '), '\u001b[2m \u001b[22m'); + assert.equal(chalk.italic(' '), '\u001b[3m \u001b[23m'); + assert.equal(chalk.inverse(' '), '\u001b[7m \u001b[27m'); + assert.equal(chalk.strikethrough(' '), '\u001b[9m \u001b[29m'); + assert.equal(chalk.underline(' '), '\u001b[4m \u001b[24m'); + }); + + it('should ok w/ 8 colors', function() { + chalk.use256 = false; + chalk.init(); + chalk.setTheme('default'); + + assert.equal(chalk.black(' '), '\u001b[30m \u001b[39m'); + assert.equal(chalk.red(' '), '\u001b[91m \u001b[39m'); + assert.equal(chalk.green(' '), '\u001b[92m \u001b[39m'); + assert.equal(chalk.yellow(' '), '\u001b[93m \u001b[39m'); + assert.equal(chalk.blue(' '), '\u001b[94m \u001b[39m'); + assert.equal(chalk.magenta(' '), '\u001b[95m \u001b[39m'); + assert.equal(chalk.cyan(' '), '\u001b[96m \u001b[39m'); + assert.equal(chalk.white(' '), '\u001b[97m \u001b[39m'); + }); + + it('should ok w/o colors', function() { + chalk.enabled = false; + chalk.init(); + chalk.setTheme('default'); + + assert.equal(chalk.black(' '), ' '); + assert.equal(chalk.red(' '), ' '); + assert.equal(chalk.green(' '), ' '); + assert.equal(chalk.yellow(' '), ' '); + assert.equal(chalk.blue(' '), ' '); + assert.equal(chalk.magenta(' '), ' '); + assert.equal(chalk.cyan(' '), ' '); + assert.equal(chalk.white(' '), ' '); + }); + + it('should sprint w/ 256 colors ok', function() { + chalk.init(); + chalk.setTheme('default'); + assert.equal(chalk.sprint(' ', '#00ff00'), '\u001b[38;5;46m \u001b[39m'); + }); + + it('should sprint w/ 8 colors ok', function() { + chalk.use256 = false; + chalk.init(); + chalk.setTheme('default'); + assert.equal(chalk.sprint(' ', '#00ff00'), '\u001b[92m \u001b[39m'); + }); + + it('should set theme ok', function() { + chalk.init(); + chalk.setTheme('dark'); + assert.equal(chalk.sprint(' ', '#009900'), chalk.green(' ')); + }); + + it('should set unknown theme ok', function() { + chalk.init(); + chalk.setTheme('unknown'); + assert.equal(chalk.sprint(' ', '#00ff00'), chalk.green(' ')); + }); +}); diff --git a/test/test_config.js b/test/test_config.js index 3b345ceb..9ae828dd 100644 --- a/test/test_config.js +++ b/test/test_config.js @@ -1,51 +1,64 @@ -var assert = require('chai').assert; -var rewire = require('rewire'); -var _ = require('underscore'); +'use strict'; +const assert = require('chai').assert; +const rewire = require('rewire'); +const _ = require('underscore'); + +const th = require('./helper'); describe('config', function() { - it('should ok w/o local config', function() { - var h = rewire('../lib/helper'); - h.getConfigFile = function() { - return 'local-config-not-exist-at-all'; - }; + let config; + const FILE = './tmp/config.json'; + + beforeEach(function() { + th.clean(); + + const file = rewire('../lib/file'); + file.configFile = () => FILE; + + config = rewire('../lib/config'); + config.__set__('file', file); + }); + + function createConfigFile(data) { + const fs = require('fs'); + fs.writeFileSync(FILE, JSON.stringify(data)); + } - var config = rewire('../lib/config'); - config.__set__('h', h); + it('should ok w/o local config', function() { + const DEFAULT_CONFIG = config.__get__('DEFAULT_CONFIG'); config.init(); - var expect = config.getDefaultConfig(); - var actual = _.extendOwn({}, config); // remove 'init' function + let actual = config.getAll(); + let expect = DEFAULT_CONFIG; assert.deepEqual(actual, expect); - expect = config.getUserConfig(); - actual = config.__get__('DEFAULT_USER_CONFIG'); + actual = config.getAll(true); + expect = _.omit(expect, 'sys'); assert.deepEqual(actual, expect); }); it('should ok w/ local config', function() { - var localConfig = { - AUTO_LOGIN: false, - LANG: 'ruby', - USE_COLOR: false - }; - - var h = rewire('../lib/helper'); - h.getFileData = function() { - return JSON.stringify(localConfig); - }; - - var config = rewire('../lib/config'); - config.__set__('h', h); + createConfigFile({ + autologin: {enable: false}, + code: {lang: 'ruby'}, + color: {enable: false} + }); config.init(); - var expect = config.getDefaultConfig(); - var actual = _.extendOwn({}, config); // remove 'init' function - _.extendOwn(expect, localConfig); - assert.deepEqual(actual, expect); + assert.equal(config.autologin.enable, false); + assert.equal(config.code.lang, 'ruby'); + assert.equal(config.color.enable, false); + assert.equal(config.code.editor, 'vim'); + }); - expect = config.getUserConfig(); - actual = config.__get__('DEFAULT_USER_CONFIG'); - _.extendOwn(actual, localConfig); - assert.deepEqual(actual, expect); + it('should remove legacy keys', function() { + createConfigFile({ + USE_COLOR: true, + code: {lang: 'ruby'} + }); + config.init(); + + assert.equal(config.USE_COLOR, undefined); + assert.equal(config.code.lang, 'ruby'); }); }); diff --git a/test/test_core.js b/test/test_core.js index fdf36da7..0a436bb4 100644 --- a/test/test_core.js +++ b/test/test_core.js @@ -1,69 +1,140 @@ -var fs = require('fs'); - -var _ = require('underscore'); -var assert = require('chai').assert; -var rewire = require('rewire'); - -var log = require('../lib/log'); - -var session = rewire('../lib/session'); -var plugin = rewire('../lib/core'); +'use strict'; +const assert = require('chai').assert; +const rewire = require('rewire'); describe('core', function() { - var PROBLEMS = [ - {id: 0, name: 'name0', slug: 'slug0', starred: false, category: 'algorithms'}, - {id: 1, name: 'name1', slug: 'slug1', starred: true, category: 'algorithms'} + let core; + let next; + + const PROBLEMS = [ + { + category: 'algorithms', + id: 0, + fid: 0, + name: 'name0', + slug: 'slug0', + level: 'Hard', + locked: true, + starred: false, + state: 'ac', + tags: ['google', 'facebook'] + }, + { + category: 'algorithms', + companies: ['amazon', 'facebook'], + id: 1, + fid: 1, + name: 'name1', + slug: 'slug1', + level: 'Easy', + locked: false, + starred: true, + state: 'none' + } ]; - var USER = {}; - var NEXT = {}; before(function() { + const log = require('../lib/log'); log.init(); - - session.getUser = function() { - return USER; - }; - - plugin.__set__('session', session); - plugin.setNext(NEXT); }); beforeEach(function() { - NEXT.getProblems = function(cb) { - return cb(null, PROBLEMS); - }; - NEXT.getProblem = function(problem, cb) { - return cb(null, problem); - }; + next = {}; + next.getProblems = cb => cb(null, PROBLEMS); + next.getProblem = (p, cb) => cb(null, p); + + core = rewire('../lib/core'); + core.setNext(next); }); + describe('#filterProblems', function() { + it('should filter by query ok', function(done) { + const cases = [ + ['', [0, 1]], + ['x', [0, 1]], + ['h', [0]], + ['H', [1]], + ['m', []], + ['M', [0, 1]], + ['l', [0]], + ['L', [1]], + ['s', [1]], + ['S', [0]], + ['d', [0]], + ['D', [1]], + ['eLsD', [1]], + ['Dh', []] + ]; + let n = cases.length; + + for (let x of cases) { + core.filterProblems({query: x[0]}, function(e, problems) { + assert.notExists(e); + assert.equal(problems.length, x[1].length); + + for (let i = 0; i < problems.length; ++i) + assert.equal(problems[i], PROBLEMS[x[1][i]]); + if (--n === 0) done(); + }); + } + }); + + it('should filter by tag ok', function(done) { + const cases = [ + [[], [0, 1]], + [['facebook'], [0, 1]], + [['google'], [0]], + [['amazon'], [1]], + [['apple'], []], + ]; + let n = cases.length; + + for (let x of cases) { + core.filterProblems({tag: x[0]}, function(e, problems) { + assert.notExists(e); + assert.equal(problems.length, x[1].length); + + for (let i = 0; i < problems.length; ++i) + assert.equal(problems[i], PROBLEMS[x[1][i]]); + if (--n === 0) done(); + }); + } + }); + + it('should fail if getProblems error', function(done) { + next.getProblems = cb => cb('getProblems error'); + core.filterProblems({}, function(e) { + assert.equal(e, 'getProblems error'); + done(); + }); + }); + }); // #filterProblems + describe('#starProblem', function() { - it('should starProblem ok', function(done) { - NEXT.starProblem = function(problem, starred, cb) { - return cb(null, starred); - }; + it('should ok', function(done) { + next.starProblem = (p, starred, cb) => cb(null, starred); assert.equal(PROBLEMS[0].starred, false); - plugin.starProblem(PROBLEMS[0], true, function(e, starred) { - assert.equal(e, null); + core.starProblem(PROBLEMS[0], true, function(e, starred) { + assert.notExists(e); assert.equal(starred, true); done(); }); }); - it('should starProblem ok if already starred', function(done) { + it('should ok if already starred', function(done) { assert.equal(PROBLEMS[1].starred, true); - plugin.starProblem(PROBLEMS[1], true, function(e, starred) { - assert.equal(e, null); + core.starProblem(PROBLEMS[1], true, function(e, starred) { + assert.notExists(e); assert.equal(starred, true); done(); }); }); - it('should starProblem ok if already unstarred', function(done) { + it('should ok if already unstarred', function(done) { assert.equal(PROBLEMS[0].starred, false); - plugin.starProblem(PROBLEMS[0], false, function(e, starred) { - assert.equal(e, null); + core.starProblem(PROBLEMS[0], false, function(e, starred) { + assert.notExists(e); assert.equal(starred, false); done(); }); @@ -71,35 +142,81 @@ describe('core', function() { }); // #starProblem describe('#exportProblem', function() { - function injectVerify(expected, done) { - plugin.__set__('fs', { - writeFileSync: function(f, data) { - assert.equal(data, expected); - done(); - }, - readFileSync: fs.readFileSync - }); - } + let file; + + beforeEach(function() { + file = rewire('../lib/file'); + file.init(); + core.__set__('file', file); + }); - it('should ok w/ code only', function(done) { - var expected = [ + it('should codeonly ok', function() { + file.isWindows = () => false; + + const expected = [ + '/**', + ' * Definition for singly-linked list.', + ' * struct ListNode {', + ' * int val;', + ' * ListNode *next;', + ' * ListNode(int x) : val(x), next(NULL) {}', + ' * };', + ' */', 'class Solution {', 'public:', ' ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {', - '', + ' ', ' }', - '};' + '};', + '' ].join('\n'); - injectVerify(expected, done); + const problem = require('./mock/add-two-numbers.20161015.json'); + const opts = { + lang: 'cpp', + code: problem.templates[0].defaultCode, + tpl: 'codeonly' + }; + assert.equal(core.exportProblem(problem, opts), expected); + }); + + it('should codeonly ok in windows', function() { + file.isWindows = () => true; + + const expected = [ + '/**', + ' * Definition for singly-linked list.', + ' * struct ListNode {', + ' * int val;', + ' * ListNode *next;', + ' * ListNode(int x) : val(x), next(NULL) {}', + ' * };', + ' */', + 'class Solution {', + 'public:', + ' ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {', + ' ', + ' }', + '};', + '' + ].join('\r\n'); - var problem = require('./mock/add-two-numbers.20161015.json'); - plugin.exportProblem(problem, 'test.cpp', true); + const problem = require('./mock/add-two-numbers.20161015.json'); + const opts = { + lang: 'cpp', + code: problem.templates[0].defaultCode, + tpl: 'codeonly' + }; + assert.equal(core.exportProblem(problem, opts), expected); }); - it('should ok w/ detailed comments', function(done) { - var expected = [ + it('should detailed ok with cpp', function() { + file.isWindows = () => false; + + const expected = [ '/*', + ' * @lc app=leetcode id=2 lang=cpp', + ' *', ' * [2] Add Two Numbers', ' *', ' * https://leetcode.com/problems/add-two-numbers', @@ -117,23 +234,38 @@ describe('core', function() { ' * Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)', ' * Output: 7 -> 0 -> 8', ' */', + '/**', + ' * Definition for singly-linked list.', + ' * struct ListNode {', + ' * int val;', + ' * ListNode *next;', + ' * ListNode(int x) : val(x), next(NULL) {}', + ' * };', + ' */', 'class Solution {', 'public:', ' ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {', - '', + ' ', ' }', '};', '' ].join('\n'); - injectVerify(expected, done); - - var problem = require('./mock/add-two-numbers.20161015.json'); - plugin.exportProblem(problem, 'test.cpp', false); + const problem = require('./mock/add-two-numbers.20161015.json'); + const opts = { + lang: 'cpp', + code: problem.templates[0].defaultCode, + tpl: 'detailed' + }; + assert.equal(core.exportProblem(problem, opts), expected); }); - it('should ok w/ detailed comments, 2nd', function(done) { - var expected = [ + it('should detailed ok with ruby', function() { + file.isWindows = () => false; + + const expected = [ + '#', + '# @lc app=leetcode id=2 lang=ruby', '#', '# [2] Add Two Numbers', '#', @@ -170,74 +302,62 @@ describe('core', function() { '' ].join('\n'); - injectVerify(expected, done); - - var problem = require('./mock/add-two-numbers.20161015.json'); + const problem = require('./mock/add-two-numbers.20161015.json'); problem.testcase = null; - problem.code = _.find(problem.templates, function(template) { - return template.value === 'ruby'; - }).defaultCode; - plugin.exportProblem(problem, 'test.rb', false); + const opts = { + lang: 'ruby', + code: problem.templates[6].defaultCode, + tpl: 'detailed' + }; + assert.equal(core.exportProblem(problem, opts), expected); }); }); // #exportProblem describe('#getProblem', function() { - it('should getProblem by id ok', function(done) { - plugin.getProblem(0, function(e, problem) { - assert.equal(e, null); + it('should get by id ok', function(done) { + core.getProblem(0, function(e, problem) { + assert.notExists(e); assert.deepEqual(problem, PROBLEMS[0]); done(); }); }); - it('should getProblem by key ok', function(done) { - plugin.getProblem('slug0', function(e, problem) { - assert.equal(e, null); + it('should get by key ok', function(done) { + core.getProblem('slug0', function(e, problem) { + assert.notExists(e); assert.deepEqual(problem, PROBLEMS[0]); done(); }); }); - it('should getProblem error if not found', function(done) { - plugin.getProblem(3, function(e, problem) { + it('should fail if not found', function(done) { + core.getProblem(3, function(e, problem) { assert.equal(e, 'Problem not found!'); done(); }); }); - it('should getProblem fail if client error', function(done) { - NEXT.getProblem = function(problem, cb) { - return cb('client getProblem error'); - }; + it('should fail if client error', function(done) { + next.getProblem = (problem, cb) => cb('client getProblem error'); - plugin.getProblem(0, function(e, problem) { + core.getProblem(0, function(e, problem) { assert.equal(e, 'client getProblem error'); done(); }); }); - it('should getProblem random ok', function(done) { - NEXT.getProblems = function(cb) { - return cb(null, [ - {id: 0, state: 'ac', locked: false}, - {id: 1, state: 'none', locked: true}, - {id: 2, state: 'none', locked: false} - ]); - }; - - plugin.getProblem(undefined, function(e, problem) { - assert.equal(e, null); - assert.equal(problem.id, 2); + it('should ok if problem is already there', function(done) { + core.getProblem(PROBLEMS[1], function(e, problem) { + assert.notExists(e); + assert.deepEqual(problem, PROBLEMS[1]); done(); }); }); - it('should getProblem fail if getProblems error', function(done) { - NEXT.getProblems = function(cb) { - return cb('getProblems error'); - }; + it('should fail if getProblems error', function(done) { + next.getProblems = cb => cb('getProblems error'); - plugin.getProblem(0, function(e, problem) { + core.getProblem(0, function(e, problem) { assert.equal(e, 'getProblems error'); done(); }); diff --git a/test/test_file.js b/test/test_file.js new file mode 100644 index 00000000..d458e83f --- /dev/null +++ b/test/test_file.js @@ -0,0 +1,163 @@ +'use strict'; +const fs = require('fs'); +const path = require('path'); + +const assert = require('chai').assert; +const rewire = require('rewire'); + +const th = require('./helper'); + +describe('file', function() { + let file; + + beforeEach(function() { + file = rewire('../lib/file'); + }); + + describe('#dirAndFiles', function() { + const HOME = path.join(__dirname, '..'); + + it('should ok on linux', function() { + if (file.isWindows()) this.skip(); + process.env.HOME = '/home/skygragon'; + + assert.equal(file.userHomeDir(), '/home/skygragon'); + assert.equal(file.homeDir(), '/home/skygragon/.lc'); + assert.equal(file.cacheDir(), '/home/skygragon/.lc/leetcode/cache'); + assert.equal(file.cacheFile('xxx'), '/home/skygragon/.lc/leetcode/cache/xxx.json'); + assert.equal(file.configFile(), '/home/skygragon/.lc/config.json'); + assert.equal(file.name('/home/skygragon/.lc/leetcode/cache/xxx.json'), 'xxx'); + }); + + it('should ok on windows', function() { + if (!file.isWindows()) this.skip(); + process.env.HOME = ''; + process.env.USERPROFILE = 'C:\\Users\\skygragon'; + assert.equal(file.userHomeDir(), 'C:\\Users\\skygragon'); + assert.equal(file.homeDir(), 'C:\\Users\\skygragon\\.lc'); + assert.equal(file.cacheDir(), 'C:\\Users\\skygragon\\.lc\\leetcode\\cache'); + assert.equal(file.cacheFile('xxx'), 'C:\\Users\\skygragon\\.lc\\leetcode\\cache\\xxx.json'); + assert.equal(file.configFile(), 'C:\\Users\\skygragon\\.lc\\config.json'); + assert.equal(file.name('C:\\Users\\skygragon\\.lc\\leetcode\\cache\\xxx.json'), 'xxx'); + }); + + it('should codeDir ok', function() { + assert.equal(file.codeDir(), HOME); + assert.equal(file.codeDir('.'), HOME); + assert.equal(file.codeDir('icons'), path.join(HOME, 'icons')); + assert.equal(file.codeDir('lib/plugins'), path.join(HOME, 'lib', 'plugins')); + }); + + it('should listCodeDir ok', function() { + const files = file.listCodeDir('lib/plugins'); + assert.equal(files.length, 3); + assert.equal(files[0].name, 'cache'); + assert.equal(files[1].name, 'leetcode'); + assert.equal(files[2].name, 'retry'); + }); + + it('should pluginFile ok', function() { + const expect = path.join(HOME, 'lib/plugins/cache.js'); + assert.equal(file.pluginFile('cache.js'), expect); + assert.equal(file.pluginFile('./cache.js'), expect); + assert.equal(file.pluginFile('https://github.com/skygragon/cache.js'), expect); + }); + + it('should data ok with missing file', function() { + assert.equal(file.data('non-exist'), null); + }); + }); // #dirAndFiles + + describe('#meta', function() { + it('should meta ok within file content', function() { + file.data = x => [ + '/ *', + ' * @lc app=leetcode id=123 lang=javascript', + ' * /' + ].join('\n'); + const meta = file.meta('dummy'); + assert.equal(meta.app, 'leetcode') + assert.equal(meta.id, '123'); + assert.equal(meta.lang, 'javascript'); + }); + + it('should meta ok with white space', function() { + file.data = x => [ + '/ *', + ' * @lc app=leetcode id=123\t \t lang=javascript\r', + ' * /' + ].join('\n'); + const meta = file.meta('dummy'); + assert.equal(meta.app, 'leetcode') + assert.equal(meta.id, '123'); + assert.equal(meta.lang, 'javascript'); + }); + + it('should meta ok within file name', function() { + file.data = x => [ + '/ *', + ' * no meta app=leetcode id=123 lang=javascript', + ' * /' + ].join('\n'); + const meta = file.meta('321.dummy.py'); + assert(!meta.app) + assert.equal(meta.id, '321'); + assert.equal(meta.lang, 'python'); + }); + + it('should meta ok within deprecated file name', function() { + file.data = x => [ + '/ *', + ' * no meta app=leetcode id=123 lang=javascript', + ' * /' + ].join('\n'); + + var meta = file.meta('111.dummy.py3'); + assert(!meta.app) + assert.equal(meta.id, '111'); + assert.equal(meta.lang, 'python3'); + + meta = file.meta('222.dummy.python3.py'); + assert(!meta.app) + assert.equal(meta.id, '222'); + assert.equal(meta.lang, 'python3'); + }); + + it('should fmt ok', function() { + file.init(); + const data = file.fmt('${id}', {id: 123}); + assert.equal(data, '123'); + }); + }); // #meta + + describe('#genneral', function() { + beforeEach(function() { + th.clean(); + }); + afterEach(function() { + th.clean(); + }); + + it('should mkdir ok', function() { + const dir = th.DIR + 'dir'; + assert.equal(fs.existsSync(dir), false); + file.mkdir(dir); + assert.equal(fs.existsSync(dir), true); + file.mkdir(dir); + assert.equal(fs.existsSync(dir), true); + }); + + it('should mv ok', function() { + const SRC = th.Dir + 'src'; + const DST = th.DIR + 'dst'; + assert.equal(fs.existsSync(SRC), false); + assert.equal(fs.existsSync(DST), false); + file.mkdir(SRC); + assert.equal(fs.existsSync(SRC), true); + assert.equal(fs.existsSync(DST), false); + file.mv(SRC, DST); + assert.equal(fs.existsSync(SRC), false); + assert.equal(fs.existsSync(DST), true); + }); + }); // #general +}); diff --git a/test/test_helper.js b/test/test_helper.js index 7ca3bef2..143bda9e 100644 --- a/test/test_helper.js +++ b/test/test_helper.js @@ -1,11 +1,21 @@ -var assert = require('chai').assert; +'use strict'; +const assert = require('chai').assert; +const rewire = require('rewire'); +const _ = require('underscore'); -var chalk = require('../lib/chalk'); -var h = require('../lib/helper'); - -chalk.init(); +const chalk = require('../lib/chalk'); describe('helper', function() { + let h; + + before(function() { + chalk.init(); + }); + + beforeEach(function() { + h = rewire('../lib/helper'); + }); + describe('#prettyState', function() { it('should ok w/ color', function() { chalk.enabled = true; @@ -46,6 +56,20 @@ describe('helper', function() { }); }); // #prettyText + describe('#prettyLevel', function() { + it('should ok w/ color', function() { + chalk.enabled = true; + + assert.equal(h.prettyLevel('Easy'), chalk.green('Easy')); + assert.equal(h.prettyLevel('Medium'), chalk.yellow('Medium')); + assert.equal(h.prettyLevel('Hard'), chalk.red('Hard')); + assert.equal(h.prettyLevel('easy '), chalk.green('easy ')); + assert.equal(h.prettyLevel('medium'), chalk.yellow('medium')); + assert.equal(h.prettyLevel('hard '), chalk.red('hard ')); + assert.equal(h.prettyLevel('unknown'), 'unknown'); + }); + }); // #prettyLevel + describe('#prettySize', function() { it('should ok', function() { assert.equal(h.prettySize(0), '0.00B'); @@ -104,12 +128,13 @@ describe('helper', function() { assert.equal(h.langToExt('java'), '.java'); assert.equal(h.langToExt('javascript'), '.js'); assert.equal(h.langToExt('mysql'), '.sql'); + assert.equal(h.langToExt('php'), '.php'); assert.equal(h.langToExt('python'), '.py'); - assert.equal(h.langToExt('python3'), '.py3'); + assert.equal(h.langToExt('python3'), '.py'); assert.equal(h.langToExt('ruby'), '.rb'); + assert.equal(h.langToExt('rust'), '.rs'); assert.equal(h.langToExt('scala'), '.scala'); assert.equal(h.langToExt('swift'), '.swift'); - assert.equal(h.langToExt('rust'), '.raw'); }); }); // #langToExt @@ -122,20 +147,22 @@ describe('helper', function() { assert.equal(h.extToLang('../file.go'), 'golang'); assert.equal(h.extToLang('file.java'), 'java'); assert.equal(h.extToLang('c:/file.js'), 'javascript'); + assert.equal(h.extToLang('~/leetcode/../file.sql'), 'mysql'); + assert.equal(h.extToLang('~/leetcode/hello.php'), 'php'); assert.equal(h.extToLang('c:/Users/skygragon/file.py'), 'python'); - assert.equal(h.extToLang('c:/Users/skygragon/file.py3'), 'python3'); assert.equal(h.extToLang('~/file.rb'), 'ruby'); + assert.equal(h.extToLang('~/leetcode/file.rs'), 'rust'); assert.equal(h.extToLang('/tmp/file.scala'), 'scala'); assert.equal(h.extToLang('~/leetcode/file.swift'), 'swift'); - assert.equal(h.extToLang('~/leetcode/../file.sql'), 'mysql'); assert.equal(h.extToLang('/home/skygragon/file.dat'), 'unknown'); }); }); // #extToLang describe('#langToCommentStyle', function() { it('should ok', function() { - var C_STYLE = {start: '/*', line: ' *', end: ' */'}; - var RUBY_STYLE = {start: '#', line: '#', end: '#'}; + const C_STYLE = {start: '/*', line: ' *', end: ' */'}; + const RUBY_STYLE = {start: '#', line: '#', end: '#'}; + const SQL_STYLE = {start: '--', line: '--', end: '--'}; assert.deepEqual(h.langToCommentStyle('bash'), RUBY_STYLE); assert.deepEqual(h.langToCommentStyle('c'), C_STYLE); @@ -144,48 +171,26 @@ describe('helper', function() { assert.deepEqual(h.langToCommentStyle('golang'), C_STYLE); assert.deepEqual(h.langToCommentStyle('java'), C_STYLE); assert.deepEqual(h.langToCommentStyle('javascript'), C_STYLE); - assert.deepEqual(h.langToCommentStyle('mysql'), RUBY_STYLE); + assert.deepEqual(h.langToCommentStyle('mysql'), SQL_STYLE); + assert.deepEqual(h.langToCommentStyle('php'), C_STYLE); assert.deepEqual(h.langToCommentStyle('python'), RUBY_STYLE); assert.deepEqual(h.langToCommentStyle('python3'), RUBY_STYLE); assert.deepEqual(h.langToCommentStyle('ruby'), RUBY_STYLE); + assert.deepEqual(h.langToCommentStyle('rust'), C_STYLE); assert.deepEqual(h.langToCommentStyle('scala'), C_STYLE); assert.deepEqual(h.langToCommentStyle('swift'), C_STYLE); }); }); // #langToCommentStyle - describe('#dirAndFiles', function() { - it('should ok', function() { - process.env.HOME = '/home/skygragon'; - - assert.equal(h.getHomeDir(), '/home/skygragon'); - assert.equal(h.getCacheDir(), '/home/skygragon/.lc'); - assert.equal(h.getCacheFile('xxx'), '/home/skygragon/.lc/xxx.json'); - assert.equal(h.getConfigFile(), '/home/skygragon/.lcconfig'); - assert.equal(h.getFilename('/home/skygragon/.lc/xxx.json'), 'xxx'); - - process.env.HOME = ''; - process.env.USERPROFILE = 'C:\\Users\\skygragon'; - assert.equal(h.getHomeDir(), 'C:\\Users\\skygragon'); - }); - - it('should getDirData ok', function() { - var files = h.getDirData(['lib', 'plugins']); - assert.equal(files.length, 3); - assert.equal(files[0].name, 'cache'); - assert.equal(files[1].name, 'leetcode'); - assert.equal(files[2].name, 'retry'); - }); - }); // #dirAndFiles - describe('#getSetCookieValue', function() { it('should ok', function() { - var resp = { + const resp = { headers: {'set-cookie': [ 'key1=value1; path=/; Httponly', 'key2=value2; path=/; Httponly'] } }; - var respNoSetCookie = { + const respNoSetCookie = { headers: {} }; @@ -198,14 +203,14 @@ describe('helper', function() { describe('#printSafeHTTP', function() { it('should hide sensitive info', function() { - var raw = [ + const raw = [ "Cookie: 'xxxxxx'", "'X-CSRFToken': 'yyyyyy'", "'set-cookie': ['zzzzzz']" ].join('\r\n'); - var hide = [ - "Cookie: