chai = require 'chai' should = chai.should() describe 'Profiles', -> Profiles = require '../src/profiles' Conditions = require '../src/conditions' U2 = require 'uglify-js' ruleListResult = (profileName, source) -> profileName: profileName source: source testProfile = (profile, request, expected, expectedCompiled) -> o_request = request if typeof request == 'string' request = Conditions.requestFromUrl(request) expectedCompiled ?= expected[0] ? Profiles.nameAsKey(expected.profileName) compiled = Profiles.compile(profile) compileResult = eval '(' + compiled.print_to_string() + ')' if typeof compileResult == 'function' compileResult = compileResult(request.url, request.host, request.scheme) if expected? matchResult = Profiles.match(profile, request) try if expected.source? chai.assert.equal(matchResult.profileName, expected.profileName) chai.assert.equal(matchResult.source, expected.source) else chai.assert.deepEqual(matchResult, expected) catch _ printResult = JSON.stringify(matchResult) msg = ("expect profile to return #{JSON.stringify(expected)} " + "instead of #{printResult} for request #{o_request}") chai.assert(false, msg) if compileResult != expectedCompiled msg = ("expect COMPILED profile to return #{expectedCompiled} " + "instead of #{compileResult} for request #{o_request}") chai.assert(false, msg) return expected describe '#pacResult', -> it 'should return DIRECT for no proxy', -> Profiles.pacResult().should.equal("DIRECT") it 'should return a valid PAC result for a proxy', -> proxy = {scheme: "http", host: "127.0.0.1", port: 8888} Profiles.pacResult(proxy).should.equal("PROXY 127.0.0.1:8888") it 'should return special compatible result for SOCKS5', -> proxy = {scheme: "socks5", host: "127.0.0.1", port: 8888} compatibleResult = "SOCKS5 127.0.0.1:8888; SOCKS 127.0.0.1:8888" Profiles.pacResult(proxy).should.equal(compatibleResult) describe '#byName', -> it 'should get profiles from builtin profiles', -> profile = Profiles.byName('direct') profile.should.be.an('object') profile.profileType.should.equal('DirectProfile') it 'should get profiles from given options', -> profile = {} profile = Profiles.byName('profile', {"+profile": profile}) profile.should.equal(profile) describe '#allReferenceSet', -> profile = Profiles.create('test', 'VirtualProfile') profile.defaultProfileName = 'bogus' it 'should throw if referenced profile does not exist', -> getAllReferenceSet = -> Profiles.allReferenceSet(profile, {}) getAllReferenceSet.should.throw(Error) it 'should process a dumb profile for each missing profile if requested', -> profile.defaultProfileName = 'bogus' refs = Profiles.allReferenceSet profile, {}, profileNotFound: 'dumb' refs['+bogus'].should.equal('bogus') describe 'SystemProfile', -> it 'should be builtin with the name "system"', -> profile = Profiles.byName('system') profile.should.be.an('object') profile.profileType.should.equal('SystemProfile') it 'should not match request to profiles', -> profile = Profiles.byName('system') should.not.exist Profiles.match(profile, {}) it 'should throw when trying to compile', -> profile = Profiles.byName('system') should.throw(-> Profiles.compile(profile)) describe 'DirectProfile', -> it 'should be builtin with the name "direct"', -> profile = Profiles.byName('direct') profile.should.be.an('object') profile.profileType.should.equal('DirectProfile') it 'should return "DIRECT" when compiled', -> profile = Profiles.byName('direct') testProfile(profile, {}, null, 'DIRECT') describe 'FixedProfile', -> profile = profileType: 'FixedProfile' bypassList: [{ conditionType: 'BypassCondition' pattern: '' }] proxyForHttp: scheme: 'socks4' host: '127.0.0.1' port: 1234 proxyForHttps: scheme: 'http' host: '127.0.0.1' port: 2345 fallbackProxy: scheme: 'socks4' host: '127.0.0.1' port: 3456 auth: proxyForHttps: username: 'test' password: 'cheesecake' it 'should use protocol-specific proxies if suitable', -> testProfile(profile, 'https://www.example.com/', ['PROXY 127.0.0.1:2345', 'https', profile.proxyForHttps, profile.auth.proxyForHttps]) it 'should use fallback proxies for other protocols', -> testProfile(profile, 'ftp://www.example.com/', ['SOCKS 127.0.0.1:3456', '', profile.fallbackProxy, undefined]) it 'should not return authentication if not provided for protocol', -> testProfile(profile, 'http://www.example.com/', ['SOCKS 127.0.0.1:1234', 'http', profile.proxyForHttp, undefined]) it 'should not use any proxy for requests matching the bypassList', -> testProfile profile, 'ftp://localhost/', ['DIRECT', profile.bypassList[0], {scheme: 'direct'}, undefined] describe 'PacProfile', -> profile = Profiles.create('test', 'PacProfile') profile.pacScript = ''' function FindProxyForURL(url, host) { return "PROXY " + host + ":8080"; } ''' it 'should return the result of the pac script', -> testProfile(profile, 'ftp://www.example.com:9999/abc', null, 'PROXY www.example.com:8080') it 'should not fail for PAC with trailing comments', -> p = Profiles.create('test', 'PacProfile') p.pacScript = profile.pacScript + ''' // This is a trailing line comment. ''' testProfile(p, 'ftp://www.example.com:9999/abc', null, 'PROXY www.example.com:8080') p = Profiles.create('test', 'PacProfile') p.pacScript = profile.pacScript + ''' /* This is a multiline comment which is not properly closed. ''' testProfile(p, 'ftp://www.example.com:9999/abc', null, 'PROXY www.example.com:8080') it 'should return includable for non-file pacUrl', -> Profiles.isIncludable(profile).should.be.true it 'should return not includable for file: pacUrl', -> p = Profiles.create('test', 'PacProfile') p.pacUrl = 'file:///proxy.pac' Profiles.isIncludable(p).should.be.false describe 'SwitchProfile', -> profile = Profiles.create('test', 'SwitchProfile') profile.rules = [ { condition: conditionType: 'HostWildcardCondition' pattern: 'company.abc.example.com' profileName: 'company' }, { condition: conditionType: 'HostWildcardCondition' pattern: '*.example.com' profileName: 'example' }, { condition: conditionType: 'HostWildcardCondition' pattern: '*.abc.example.com' profileName: 'abc' } ] profile.defaultProfileName = 'default' it 'should match requests based on rules', -> testProfile(profile, 'http://company.abc.example.com:998/abc', profile.rules[0]) it 'should respect the order of rules', -> testProfile(profile, 'http://abc.example.com:9999/abc', profile.rules[1]) testProfile(profile, 'http://www.example.com:9999/abc', profile.rules[1]) it 'should return defaultProfileName when no rules match', -> testProfile(profile, 'http://www.example.org:9999/abc', ['+default', null]) it 'should calulate directly referenced profiles correctly', -> set = Profiles.directReferenceSet(profile) set.should.eql( '+company': 'company' '+example': 'example' '+abc': 'abc' '+default': 'default' ) it 'should clear the reference cache on profile revision change', -> profile.revision = 'a' set = Profiles.directReferenceSet(profile) # Remove 'default' from references. profile.defaultProfileName = 'abc' profile.revision = 'b' newSet = Profiles.directReferenceSet(profile) newSet.should.eql( '+company': 'company' '+example': 'example' '+abc': 'abc' ) it 'should clear the reference cache if explicitly requested', -> profile.revision = 'a' set = Profiles.directReferenceSet(profile) # Remove 'default' from references. profile.defaultProfileName = 'abc' Profiles.dropCache(profile) newSet = Profiles.directReferenceSet(profile) newSet.should.eql( '+company': 'company' '+example': 'example' '+abc': 'abc' ) describe 'VirtualProfile', -> profile = Profiles.create('test', 'VirtualProfile') profile.defaultProfileName = 'default' it 'should always return defaultProfileName', -> testProfile(profile, 'http://www.example.com/abc', ['+default', null]) describe 'RulelistProfile', -> profile = Profiles.create('test', 'AutoProxyRuleListProfile') profile.defaultProfileName = 'default' profile.matchProfileName = 'example' profile.ruleList = 'example.com' profile.revision = 'a' it 'should calulate directly referenced profiles correctly', -> set = Profiles.directReferenceSet(profile) set.should.eql( '+example': 'example' '+default': 'default' ) it 'should calulate referenced profiles for rule list with results', -> set = Profiles.directReferenceSet({ profileType: 'RuleListProfile' format: 'Switchy' matchProfileName: 'ignored' defaultProfileName: 'alsoIgnored' ruleList: ''' [SwitchyOmega Conditions] @with result !*.example.org *.example.com +ABC * +DEF ''' }) set.should.eql( '+ABC': 'ABC' '+DEF': 'DEF' ) it 'should match requests based on the rule list', -> testProfile(profile, 'http://localhost/example.com', ruleListResult('example', 'example.com')) testProfile(profile, 'http://localhost/example.org', ['+default', null]) it 'should update rule list on update', -> Profiles.update(profile, 'example.org') profile.revision = 'b' testProfile(profile, 'http://localhost/example.com', ['+default', null]) testProfile(profile, 'http://localhost/example.org', ruleListResult('example', 'example.org')) it 'should not fail when ruleList is not provided', -> p = profileType: 'RuleListProfile' format: 'Switchy' matchProfileName: 'match' defaultProfileName: 'default' Profiles.directReferenceSet(p).should.be.an 'object' testProfile(p, 'http://localhost/example.com', ['+default', null]) it 'should switch to AutoProxy format on update if detected', -> profile = Profiles.create('test2', 'RuleListProfile') profile.format = 'Switchy' profile.defaultProfileName = 'default' profile.matchProfileName = 'example' profile.format.should.equal 'Switchy' Profiles.update(profile, '[AutoProxy]\nexample.org') profile.format.should.equal 'AutoProxy' testProfile(profile, 'http://localhost/example.com', ['+default', null]) testProfile(profile, 'http://localhost/example.org', ruleListResult('example', 'example.org'))