Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F6058157
index.tests.js
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
25 KB
Referenced Files
None
Subscribers
None
index.tests.js
View Options
/* eslint-disable camelcase */
(
function
()
{
'use strict'
;
const
realAjax
=
$
.
ajax
,
realGeoIP
=
mw
.
geoIP
,
realBucketCookie
=
$
.
cookie
(
'CN'
),
realHideCookie
=
$
.
cookie
(
'centralnotice_hide_fundraising'
),
realSendBeacon
=
navigator
.
sendBeacon
,
realshouldHide
=
mw
.
centralNotice
.
internal
.
hide
.
shouldHide
,
bannerData
=
{
bannerName
:
'test_banner'
,
campaign
:
'test_campaign'
,
category
:
'test'
,
bannerHtml
:
'<div id="test_banner"></div>'
},
nowSec
=
Date
.
now
()
/
1000
,
choiceData2Campaigns
=
[
{
name
:
'campaign1'
,
preferred
:
1
,
throttle
:
100
,
bucket_count
:
1
,
geotargeted
:
false
,
start
:
nowSec
-
1
,
end
:
nowSec
+
600
,
banners
:
[
{
name
:
'banner1'
,
weight
:
25
,
bucket
:
0
,
category
:
'fundraising'
,
devices
:
[
'desktop'
],
display_anon
:
true
,
display_account
:
true
}
],
mixins
:
{}
},
{
name
:
'campaign2'
,
preferred
:
1
,
throttle
:
100
,
bucket_count
:
1
,
geotargeted
:
false
,
start
:
nowSec
-
1
,
end
:
nowSec
+
600
,
banners
:
[
{
name
:
'banner2'
,
weight
:
25
,
bucket
:
0
,
category
:
'fundraising'
,
devices
:
[
'desktop'
],
display_anon
:
true
,
display_account
:
true
}
],
mixins
:
{}
}
],
choiceData1Campaign2Banners
=
[
{
name
:
'campaign1'
,
preferred
:
1
,
throttle
:
100
,
bucket_count
:
1
,
geotargeted
:
false
,
start
:
nowSec
-
1
,
end
:
nowSec
+
600
,
banners
:
[
{
name
:
'banner1'
,
weight
:
25
,
bucket
:
0
,
category
:
'fundraising'
,
devices
:
[
'desktop'
],
display_anon
:
true
,
display_account
:
true
},
{
name
:
'banner2'
,
weight
:
25
,
bucket
:
0
,
category
:
'fundraising'
,
devices
:
[
'desktop'
],
display_anon
:
true
,
display_account
:
true
}
],
mixins
:
{
testMixin
:
[
'arg1'
,
'arg2'
]
}
}
],
choiceData1Campaign2Banners2Buckets
=
[
{
name
:
'campaign1'
,
preferred
:
1
,
throttle
:
100
,
bucket_count
:
2
,
geotargeted
:
false
,
start
:
nowSec
-
1
,
end
:
nowSec
+
600
,
banners
:
[
{
name
:
'banner1'
,
weight
:
25
,
bucket
:
0
,
category
:
'fundraising'
,
devices
:
[
'mobile'
],
display_anon
:
false
,
display_account
:
false
},
{
name
:
'banner2'
,
weight
:
25
,
bucket
:
1
,
category
:
'fundraising'
,
devices
:
[
'desktop'
],
display_anon
:
true
,
display_account
:
true
}
],
mixins
:
{
testMixin
:
[
'arg1'
,
'arg2'
]
}
}
],
choiceDataCampaignsStaleness
=
[
{
name
:
'campaign1_stale'
,
preferred
:
2
,
throttle
:
100
,
bucket_count
:
1
,
geotargeted
:
false
,
start
:
nowSec
-
(
60000
*
(
15
*
60000
)
),
// with leeway
end
:
nowSec
-
(
600
*
(
15
*
60000
)
),
// with leeway
banners
:
[
{
name
:
'banner1'
,
weight
:
25
,
bucket
:
0
,
category
:
'fundraising'
,
devices
:
[
'desktop'
],
display_anon
:
true
,
display_account
:
true
}
],
mixins
:
{}
}
],
choiceDataCampaignsFallbackMixin
=
[
{
name
:
'campaign1_mixin'
,
preferred
:
2
,
throttle
:
100
,
bucket_count
:
1
,
geotargeted
:
false
,
start
:
nowSec
-
1
,
end
:
nowSec
+
600
,
banners
:
[
{
name
:
'banner1'
,
weight
:
25
,
bucket
:
0
,
category
:
'fundraising'
,
devices
:
[
'desktop'
],
display_anon
:
true
,
display_account
:
true
}
],
mixins
:
{
testMixin
:
[
'arg1'
,
'arg2'
]
}
},
{
name
:
'campaign2'
,
preferred
:
1
,
throttle
:
100
,
bucket_count
:
1
,
geotargeted
:
false
,
start
:
nowSec
-
1
,
end
:
nowSec
+
600
,
banners
:
[
{
name
:
'banner2'
,
weight
:
25
,
bucket
:
0
,
category
:
'fundraising'
,
devices
:
[
'desktop'
],
display_anon
:
true
,
display_account
:
true
}
],
mixins
:
{}
}
],
choiceDataCampaignsFallbackHidden
=
[
{
name
:
'campaign1'
,
preferred
:
2
,
throttle
:
100
,
bucket_count
:
1
,
geotargeted
:
false
,
start
:
nowSec
-
1
,
end
:
nowSec
+
600
,
banners
:
[
{
name
:
'banner1'
,
weight
:
25
,
bucket
:
0
,
category
:
'fundraising'
,
devices
:
[
'desktop'
],
display_anon
:
true
,
display_account
:
true
}
],
mixins
:
{}
},
{
name
:
'campaign2'
,
preferred
:
1
,
throttle
:
100
,
bucket_count
:
1
,
geotargeted
:
false
,
start
:
nowSec
-
1
,
end
:
nowSec
+
600
,
banners
:
[
{
name
:
'banner2'
,
weight
:
25
,
bucket
:
0
,
category
:
'something'
,
devices
:
[
'desktop'
],
display_anon
:
true
,
display_account
:
true
}
],
mixins
:
{}
}
],
choiceDataAllCampaignsFail
=
[
{
name
:
'campaign1'
,
preferred
:
1
,
throttle
:
100
,
bucket_count
:
1
,
geotargeted
:
false
,
start
:
nowSec
-
1
,
end
:
nowSec
+
600
,
banners
:
[
{
name
:
'banner1'
,
weight
:
25
,
bucket
:
0
,
category
:
'fundraising'
,
devices
:
[
'desktop'
],
display_anon
:
true
,
display_account
:
true
}
],
mixins
:
{
testMixin
:
[]
}
},
{
name
:
'campaign2'
,
preferred
:
1
,
throttle
:
100
,
bucket_count
:
1
,
geotargeted
:
false
,
start
:
nowSec
-
1
,
end
:
nowSec
+
600
,
banners
:
[
{
name
:
'banner2'
,
weight
:
25
,
bucket
:
0
,
category
:
'fundraising'
,
devices
:
[
'desktop'
],
display_anon
:
true
,
display_account
:
true
}
],
mixins
:
{
testMixin
:
[]
}
}
],
choiceDataAllCampaignsImpressionRates
=
[
{
name
:
'campaign1'
,
preferred
:
2
,
throttle
:
100
,
bucket_count
:
1
,
geotargeted
:
false
,
start
:
nowSec
-
1
,
end
:
nowSec
+
600
,
banners
:
[
{
name
:
'banner1'
,
weight
:
25
,
bucket
:
0
,
category
:
'fundraising'
,
devices
:
[
'desktop'
],
display_anon
:
true
,
display_account
:
true
}
],
mixins
:
{
testMixinRates
:
[
0.75
]
}
},
{
name
:
'campaign2'
,
preferred
:
1
,
throttle
:
100
,
bucket_count
:
1
,
geotargeted
:
false
,
start
:
nowSec
-
1
,
end
:
nowSec
+
600
,
banners
:
[
{
name
:
'banner2'
,
weight
:
25
,
bucket
:
0
,
category
:
'fundraising'
,
devices
:
[
'desktop'
],
display_anon
:
true
,
display_account
:
true
}
],
mixins
:
{
testMixinRates
:
[
0.5
]
}
}
];
QUnit
.
module
(
'ext.centralNotice.display'
,
QUnit
.
newMwEnvironment
(
{
beforeEach
:
function
()
{
$
(
'#siteNotice'
).
remove
();
$
.
removeCookie
(
'centralnotice_hide_fundraising'
,
{
path
:
'/'
}
);
$
.
removeCookie
(
'CN'
,
{
path
:
'/'
}
);
// Suppress background calls
$
.
ajax
=
function
()
{
return
$
.
Deferred
();
};
// Create normalized siteNotice.
$
(
'#qunit-fixture'
).
append
(
'<div id=siteNotice><div id=centralNotice></div></div>'
);
// Mock mw.geoIP
mw
.
geoIP
=
{
getPromise
:
function
()
{
const
deferred
=
$
.
Deferred
();
// Resolve with minimal valid geo object
deferred
.
resolve
(
{
country
:
'AQ'
}
);
return
deferred
.
promise
();
}
};
// Reset record impression deferred object and array of promises for delaying
// record impression call
mw
.
centralNotice
.
recordImpressionDeferredObj
=
null
;
mw
.
centralNotice
.
recordImpressionDelayPromises
=
[];
},
afterEach
:
function
()
{
$
.
ajax
=
realAjax
;
mw
.
geoIP
=
realGeoIP
;
navigator
.
sendBeacon
=
realSendBeacon
;
$
.
cookie
(
'centralnotice_hide_fundraising'
,
realHideCookie
,
{
path
:
'/'
}
);
$
.
cookie
(
'CN'
,
realBucketCookie
,
{
path
:
'/'
}
);
mw
.
centralNotice
.
internal
.
state
.
data
=
{};
mw
.
centralNotice
.
internal
.
state
.
campaign
=
null
;
mw
.
centralNotice
.
internal
.
state
.
banner
=
null
;
mw
.
centralNotice
.
internal
.
state
.
urlParams
.
recordImpressionSampleRate
=
null
;
mw
.
centralNotice
.
internal
.
state
.
urlParams
.
impressionEventSampleRate
=
null
;
mw
.
centralNotice
.
internal
.
state
.
attemptedCampaignsByName
=
{};
mw
.
centralNotice
.
internal
.
hide
.
shouldHide
=
realshouldHide
;
}
}
)
);
QUnit
.
test
(
'canInsertBanner'
,
(
assert
)
=>
{
mw
.
centralNotice
.
choiceData
=
choiceData2Campaigns
;
mw
.
centralNotice
.
chooseAndMaybeDisplay
();
// We call reallyInsertBanner() instead of insertBanner() to avoid
// the async DOM-waiting of the latter.
mw
.
centralNotice
.
reallyInsertBanner
(
bannerData
);
assert
.
strictEqual
(
$
(
'div#test_banner'
).
length
,
1
);
}
);
/**
* Create the required state in CN for the record impression call to occur. The first
* campaign in choiceData2Campaigns will be chosen.
*
* @param campaignsData
*/
function
mockChoiceDataForRecordImpressionCall
(
campaignsData
)
{
// Request 100% sample rate for record impression
mw
.
centralNotice
.
internal
.
state
.
urlParams
.
recordImpressionSampleRate
=
1
;
mw
.
centralNotice
.
choiceData
=
campaignsData
;
// Set `randomcampaign` to force the same choice each time, for tests where more
// than one campaign may be available
mw
.
centralNotice
.
internal
.
state
.
urlParams
.
randomcampaign
=
0.25
;
mw
.
centralNotice
.
chooseAndMaybeDisplay
();
}
QUnit
.
test
(
'call record impression'
,
(
assert
)
=>
{
// Mock navigator.sendBeacon to capture calls and check data points sent
navigator
.
sendBeacon
=
function
(
urlString
)
{
const
url
=
new
URL
(
urlString
);
assert
.
strictEqual
(
url
.
searchParams
.
get
(
'campaign'
),
'campaign1'
,
'record impression campaign'
);
assert
.
strictEqual
(
url
.
searchParams
.
get
(
'banner'
),
'banner1'
,
'record impression banner'
);
};
mockChoiceDataForRecordImpressionCall
(
choiceData2Campaigns
);
// Call reallyInsertBanner() instead of insertBanner() to avoid the async
// DOM-waiting of the latter. This triggers the call to record impression.
mw
.
centralNotice
.
reallyInsertBanner
(
bannerData
);
}
);
QUnit
.
test
(
'delay record impression call and register tests'
,
(
assert
)
=>
{
const
deferred
=
$
.
Deferred
(),
signalTestDone
=
assert
.
async
();
// Mock navigator.sendBeacon to check record impression doesn't fire early
navigator
.
sendBeacon
=
function
()
{
// If we get here, it's a failure
assert
.
true
(
false
,
'record impression waits, as requested'
);
};
mockChoiceDataForRecordImpressionCall
(
choiceData2Campaigns
);
// Request a delay and capture the promise that should run right before the
// record impression call
const
recordImpresionPromise
=
mw
.
centralNotice
.
requestRecordImpressionDelay
(
deferred
.
promise
()
);
recordImpresionPromise
.
done
(
()
=>
{
mw
.
centralNotice
.
registerTest
(
'test_test'
);
}
);
// Call reallyInsertBanner() instead of insertBanner() to avoid the async
// DOM-waiting of the latter. This doesn't trigger the call to record impression
// yet because we requested a delay.
mw
.
centralNotice
.
reallyInsertBanner
(
bannerData
);
// Mock navigator.sendBeacon to capture calls and check data points sent
navigator
.
sendBeacon
=
function
(
urlString
)
{
const
url
=
new
URL
(
urlString
);
assert
.
strictEqual
(
url
.
searchParams
.
get
(
'campaign'
),
'campaign1'
,
'record impression campaign'
);
assert
.
strictEqual
(
url
.
searchParams
.
get
(
'banner'
),
'banner1'
,
'record impression banner'
);
assert
.
strictEqual
(
url
.
searchParams
.
get
(
'testIdentifiers'
),
'test_test'
,
'record impression test identifier'
);
signalTestDone
();
};
// Resolve the promise to let the record impression call go ahead
deferred
.
resolve
();
}
);
QUnit
.
skip
(
'record impression timeout and register tests'
,
(
assert
)
=>
{
const
start
=
Date
.
now
(),
MAX_RECORD_IMPRESSION_DELAY
=
250
,
// Coordinate with ext.centralnotice.display.js
signalTestDone
=
assert
.
async
();
mockChoiceDataForRecordImpressionCall
(
choiceData2Campaigns
);
// Request a delay and capture the promise that should run right before the
// record impression call
const
recordImpresionPromise
=
mw
.
centralNotice
.
requestRecordImpressionDelay
(
$
.
Deferred
().
promise
()
);
recordImpresionPromise
.
done
(
()
=>
{
mw
.
centralNotice
.
registerTest
(
'test_timeouted_test'
);
}
);
// Mock navigator.sendBeacon to capture calls and check time and data points sent
navigator
.
sendBeacon
=
function
(
urlString
)
{
const
url
=
new
URL
(
urlString
),
delay
=
Date
.
now
()
-
start
;
assert
.
strictEqual
(
url
.
searchParams
.
get
(
'campaign'
),
'campaign1'
,
'record impression campaign'
);
assert
.
strictEqual
(
url
.
searchParams
.
get
(
'banner'
),
'banner1'
,
'record impression banner'
);
// 50 ms leewway is bit arbitrary
assert
.
true
(
(
delay
>
MAX_RECORD_IMPRESSION_DELAY
-
50
)
&&
(
delay
<
MAX_RECORD_IMPRESSION_DELAY
+
50
),
'record impression called by timeout'
);
assert
.
strictEqual
(
url
.
query
.
testIdentifiers
,
'test_timeouted_test'
,
'record impression test identifier'
);
signalTestDone
();
};
// Call reallyInsertBanner() instead of insertBanner() to avoid the async
// DOM-waiting of the latter. This starts the record impression timeout.
mw
.
centralNotice
.
reallyInsertBanner
(
bannerData
);
// We don't resolve the promise to delay record impression. The call should
// be made by MAX_RECORD_IMPRESSION_DELAY milliseconds (give or take a bit).
}
);
QUnit
.
skip
(
'record impression called only once'
,
(
assert
)
=>
{
const
deferred
=
$
.
Deferred
(),
MAX_RECORD_IMPRESSION_DELAY
=
250
,
// Coordinate with ext.centralnotice.display.js
signalTestDone
=
assert
.
async
();
mockChoiceDataForRecordImpressionCall
(
choiceData2Campaigns
);
// Request a delay and capture the promise that should run right before the
// record impression call
const
recordImpresionPromise
=
mw
.
centralNotice
.
requestRecordImpressionDelay
(
deferred
.
promise
()
);
// Mock navigator.sendBeacon to capture calls
navigator
.
sendBeacon
=
function
()
{
assert
.
true
(
true
,
'record impression called once'
);
// Re-mock to fail test if called again
navigator
.
sendBeacon
=
function
()
{
assert
.
true
(
false
,
'record impression not called twice'
);
};
};
// Call reallyInsertBanner() instead of insertBanner() to avoid the async
// DOM-waiting of the latter. This starts the record impression timeout.
mw
.
centralNotice
.
reallyInsertBanner
(
bannerData
);
// Set another timeout to resolve the promise requesting a delay after the timeout
// to call record impression has run. By starting this timeout after the record
// impression timeout (above) and making it a bit longer, we ensure it runs second.
setTimeout
(
()
=>
{
assert
.
strictEqual
(
recordImpresionPromise
.
state
(),
'resolved'
,
'record impression call was from timeout'
);
// Resolve the promise that delays the call only up until the timeout, to
// test that record impression isn't called a second time.
deferred
.
resolve
();
},
MAX_RECORD_IMPRESSION_DELAY
+
1
);
setTimeout
(
()
=>
{
signalTestDone
();
},
MAX_RECORD_IMPRESSION_DELAY
+
2
);
}
);
QUnit
.
test
(
'banner= override param'
,
(
assert
)
=>
{
mw
.
centralNotice
.
internal
.
state
.
urlParams
.
banner
=
'test_banner'
;
$
.
ajax
=
function
(
params
)
{
assert
.
true
(
/Special(?:[:]|%3A)BannerLoader.*[?&]banner=test_banner/
.
test
(
params
.
url
)
);
return
$
.
Deferred
();
};
mw
.
centralNotice
.
displayTestingBanner
();
assert
.
true
(
mw
.
centralNotice
.
data
.
testingBanner
);
}
);
QUnit
.
test
(
'randomcampaign= override param'
,
(
assert
)
=>
{
mw
.
centralNotice
.
choiceData
=
choiceData2Campaigns
;
// Get the first campaign
mw
.
centralNotice
.
internal
.
state
.
urlParams
.
randomcampaign
=
0.25
;
$
.
ajax
=
function
(
params
)
{
assert
.
true
(
/Special(?:[:]|%3A)BannerLoader.*[?&]banner=banner1/
.
test
(
params
.
url
)
);
return
$
.
Deferred
();
};
mw
.
centralNotice
.
chooseAndMaybeDisplay
();
// Get the second campaign
mw
.
centralNotice
.
internal
.
state
.
urlParams
.
randomcampaign
=
0.75
;
$
.
ajax
=
function
(
params
)
{
assert
.
true
(
/Special(?:[:]|%3A)BannerLoader.*[?&]banner=banner2/
.
test
(
params
.
url
)
);
return
$
.
Deferred
();
};
mw
.
centralNotice
.
chooseAndMaybeDisplay
();
}
);
QUnit
.
test
(
'randombanner= override param'
,
(
assert
)
=>
{
mw
.
centralNotice
.
choiceData
=
choiceData1Campaign2Banners
;
// Get the first banner
mw
.
centralNotice
.
internal
.
state
.
urlParams
.
randombanner
=
0.25
;
$
.
ajax
=
function
(
params
)
{
assert
.
true
(
/Special(?:[:]|%3A)BannerLoader.*[?&]banner=banner1/
.
test
(
params
.
url
)
);
return
$
.
Deferred
();
};
mw
.
centralNotice
.
chooseAndMaybeDisplay
();
// Get the second banner
mw
.
centralNotice
.
internal
.
state
.
urlParams
.
randombanner
=
0.75
;
$
.
ajax
=
function
(
params
)
{
assert
.
true
(
/Special(?:[:]|%3A)BannerLoader.*[?&]banner=banner2/
.
test
(
params
.
url
)
);
return
$
.
Deferred
();
};
mw
.
centralNotice
.
chooseAndMaybeDisplay
();
}
);
QUnit
.
test
(
'runs hooks on banner shown'
,
(
assert
)
=>
{
const
mixin
=
new
mw
.
centralNotice
.
Mixin
(
'testMixin'
);
$
.
ajax
=
function
()
{
mw
.
centralNotice
.
reallyInsertBanner
(
bannerData
);
return
$
.
Deferred
();
};
mixin
.
setPreBannerHandler
(
(
params
)
=>
{
assert
.
deepEqual
(
params
,
choiceData1Campaign2Banners
[
0
].
mixins
.
testMixin
);
}
);
mixin
.
setPostBannerOrFailHandler
(
(
params
)
=>
{
assert
.
deepEqual
(
params
,
choiceData1Campaign2Banners
[
0
].
mixins
.
testMixin
);
}
);
mw
.
centralNotice
.
registerCampaignMixin
(
mixin
);
mw
.
centralNotice
.
choiceData
=
choiceData1Campaign2Banners
;
mw
.
centralNotice
.
chooseAndMaybeDisplay
();
assert
.
strictEqual
(
mw
.
centralNotice
.
data
.
status
,
mw
.
centralNotice
.
internal
.
state
.
STATUSES
.
BANNER_SHOWN
.
key
);
}
);
QUnit
.
test
(
'runs hooks on banner canceled'
,
(
assert
)
=>
{
const
mixin
=
new
mw
.
centralNotice
.
Mixin
(
'testMixin'
);
mixin
.
setPreBannerHandler
(
(
params
)
=>
{
assert
.
deepEqual
(
params
,
choiceData1Campaign2Banners
[
0
].
mixins
.
testMixin
);
mw
.
centralNotice
.
failCampaign
(
'testReason'
);
}
);
mixin
.
setPostBannerOrFailHandler
(
(
params
)
=>
{
assert
.
deepEqual
(
params
,
choiceData1Campaign2Banners
[
0
].
mixins
.
testMixin
);
}
);
mw
.
centralNotice
.
registerCampaignMixin
(
mixin
);
mw
.
centralNotice
.
choiceData
=
choiceData1Campaign2Banners
;
mw
.
centralNotice
.
chooseAndMaybeDisplay
();
assert
.
strictEqual
(
mw
.
centralNotice
.
data
.
status
,
mw
.
centralNotice
.
internal
.
state
.
STATUSES
.
BANNER_CANCELED
.
key
);
assert
.
strictEqual
(
mw
.
centralNotice
.
data
.
bannerCanceledReason
,
'testReason'
);
assert
.
true
(
mw
.
centralNotice
.
internal
.
state
.
isCampaignFailed
()
);
}
);
QUnit
.
test
(
'runs hooks on no banner available'
,
(
assert
)
=>
{
const
mixin
=
new
mw
.
centralNotice
.
Mixin
(
'testMixin'
);
mixin
.
setPreBannerHandler
(
(
params
)
=>
{
assert
.
deepEqual
(
params
,
choiceData1Campaign2Banners2Buckets
[
0
].
mixins
.
testMixin
);
}
);
mixin
.
setPostBannerOrFailHandler
(
(
params
)
=>
{
assert
.
deepEqual
(
params
,
choiceData1Campaign2Banners2Buckets
[
0
].
mixins
.
testMixin
);
}
);
mw
.
centralNotice
.
registerCampaignMixin
(
mixin
);
mw
.
centralNotice
.
choiceData
=
choiceData1Campaign2Banners2Buckets
;
mw
.
centralNotice
.
internal
.
bucketer
.
setCampaign
(
choiceData1Campaign2Banners2Buckets
[
0
]
);
mw
.
centralNotice
.
internal
.
bucketer
.
process
();
mw
.
centralNotice
.
internal
.
bucketer
.
setBucket
(
0
);
mw
.
centralNotice
.
chooseAndMaybeDisplay
();
assert
.
strictEqual
(
mw
.
centralNotice
.
data
.
status
,
mw
.
centralNotice
.
internal
.
state
.
STATUSES
.
NO_BANNER_AVAILABLE
.
key
);
}
);
QUnit
.
test
(
'short-circuit when there is a stale campaign in choices'
,
(
assert
)
=>
{
mw
.
centralNotice
.
choiceData
=
choiceDataCampaignsStaleness
;
mw
.
centralNotice
.
chooseAndMaybeDisplay
();
assert
.
strictEqual
(
mw
.
centralNotice
.
internal
.
state
.
data
.
status
,
mw
.
centralNotice
.
internal
.
state
.
STATUSES
.
CHOICE_DATA_STALE
.
key
,
'choice data is stale'
);
}
);
QUnit
.
test
(
'fallback when preferred campaign is filtered out by a mixin'
,
(
assert
)
=>
{
const
mixin
=
new
mw
.
centralNotice
.
Mixin
(
'testMixin'
);
mixin
.
setPreBannerHandler
(
()
=>
{
mw
.
centralNotice
.
failCampaign
(
'testReason'
);
}
);
mw
.
centralNotice
.
registerCampaignMixin
(
mixin
);
mw
.
centralNotice
.
choiceData
=
choiceDataCampaignsFallbackMixin
;
mw
.
centralNotice
.
chooseAndMaybeDisplay
();
assert
.
strictEqual
(
mw
.
centralNotice
.
internal
.
state
.
data
.
status
,
mw
.
centralNotice
.
internal
.
state
.
STATUSES
.
BANNER_CHOSEN
.
key
,
'banner is chosen'
);
assert
.
strictEqual
(
mw
.
centralNotice
.
internal
.
state
.
data
.
banner
,
'banner2'
);
assert
.
strictEqual
(
mw
.
centralNotice
.
internal
.
state
.
campaign
.
name
,
'campaign2'
,
'campaign is chosen correctly'
);
}
);
QUnit
.
test
(
'fallback when preferred campaign banner is hidden'
,
(
assert
)
=>
{
let
i
=
0
;
mw
.
centralNotice
.
internal
.
hide
.
shouldHide
=
function
()
{
i
++
;
// Ensure only the first campaign from the list will hit hide.shouldHide()
return
i
===
1
;
};
mw
.
centralNotice
.
choiceData
=
choiceDataCampaignsFallbackHidden
;
mw
.
centralNotice
.
chooseAndMaybeDisplay
();
assert
.
strictEqual
(
mw
.
centralNotice
.
internal
.
state
.
data
.
status
,
mw
.
centralNotice
.
internal
.
state
.
STATUSES
.
BANNER_CHOSEN
.
key
,
'banner is chosen'
);
assert
.
strictEqual
(
mw
.
centralNotice
.
internal
.
state
.
data
.
banner
,
'banner2'
);
assert
.
strictEqual
(
mw
.
centralNotice
.
internal
.
state
.
campaign
.
name
,
'campaign2'
,
'campaign is chosen correctly'
);
}
);
QUnit
.
test
(
'record impression is being called when all campaigns fail'
,
function
(
assert
)
{
const
mixin
=
new
mw
.
centralNotice
.
Mixin
(
'testMixin'
);
this
.
sandbox
.
stub
(
navigator
,
'sendBeacon'
,
()
=>
{
assert
.
step
(
'beacon'
);
}
);
// Make every campaign fail by cancelling banners
mixin
.
setPreBannerHandler
(
()
=>
{
mw
.
centralNotice
.
failCampaign
(
'testReason'
);
}
);
mw
.
centralNotice
.
registerCampaignMixin
(
mixin
);
mockChoiceDataForRecordImpressionCall
(
choiceDataAllCampaignsFail
);
// Expect sendBeacon to be called once
assert
.
verifySteps
(
[
'beacon'
]
);
}
);
QUnit
.
test
(
'impression sample rate is kept as highest value'
,
(
assert
)
=>
{
const
mixin
=
new
mw
.
centralNotice
.
Mixin
(
'testMixinRates'
);
// Set new sample rates via mixin
mixin
.
setPreBannerHandler
(
(
mixinParams
)
=>
{
mw
.
centralNotice
.
setMinRecordImpressionSampleRate
(
mixinParams
[
0
]
);
// Fail campaign by cancelling a banner
mw
.
centralNotice
.
failCampaign
(
'testReason'
);
}
);
mw
.
centralNotice
.
registerCampaignMixin
(
mixin
);
mw
.
centralNotice
.
choiceData
=
choiceDataAllCampaignsImpressionRates
;
mw
.
centralNotice
.
chooseAndMaybeDisplay
();
assert
.
strictEqual
(
mw
.
centralNotice
.
internal
.
state
.
getData
().
recordImpressionSampleRate
,
0.75
);
}
);
QUnit
.
test
(
'event sample rate is kept as highest value'
,
(
assert
)
=>
{
const
mixin
=
new
mw
.
centralNotice
.
Mixin
(
'testMixinRates'
);
// Set new sample rates via mixin
mixin
.
setPreBannerHandler
(
(
mixinParams
)
=>
{
mw
.
centralNotice
.
setMinImpressionEventSampleRate
(
mixinParams
[
0
]
);
// Fail campaign by cancelling a banner
mw
.
centralNotice
.
failCampaign
(
'testReason'
);
}
);
mw
.
centralNotice
.
registerCampaignMixin
(
mixin
);
mw
.
centralNotice
.
choiceData
=
choiceDataAllCampaignsImpressionRates
;
mw
.
centralNotice
.
chooseAndMaybeDisplay
();
assert
.
strictEqual
(
mw
.
centralNotice
.
internal
.
state
.
getData
().
impressionEventSampleRate
,
0.75
);
}
);
QUnit
.
test
(
'event sample rate is being correctly overridden from url'
,
(
assert
)
=>
{
const
mixin
=
new
mw
.
centralNotice
.
Mixin
(
'testMixinRates'
);
// Override rate via URL param
mw
.
centralNotice
.
internal
.
state
.
urlParams
.
impressionEventSampleRate
=
1
;
// Set new sample rates via mixin
mixin
.
setPreBannerHandler
(
(
mixinParams
)
=>
{
mw
.
centralNotice
.
setMinImpressionEventSampleRate
(
mixinParams
[
0
]
);
// Fail campaign by cancelling a banner
mw
.
centralNotice
.
failCampaign
(
'testReason'
);
}
);
mw
.
centralNotice
.
registerCampaignMixin
(
mixin
);
mw
.
centralNotice
.
choiceData
=
choiceDataAllCampaignsImpressionRates
;
mw
.
centralNotice
.
chooseAndMaybeDisplay
();
assert
.
strictEqual
(
mw
.
centralNotice
.
internal
.
state
.
getData
().
impressionEventSampleRate
,
1
);
}
);
QUnit
.
test
(
'campaign statuses are tracked as serialized JSON string'
,
(
assert
)
=>
{
const
mixin
=
new
mw
.
centralNotice
.
Mixin
(
'testMixin'
);
// Make every campaign fail by cancelling banners
mixin
.
setPreBannerHandler
(
()
=>
{
mw
.
centralNotice
.
failCampaign
(
'testReason'
);
}
);
// Mock navigator.sendBeacon
navigator
.
sendBeacon
=
function
(
urlString
)
{
const
url
=
new
URL
(
urlString
);
const
statuses
=
JSON
.
parse
(
url
.
searchParams
.
get
(
'campaignStatuses'
)
);
assert
.
strictEqual
(
statuses
.
length
,
2
,
'correct amount of records'
);
};
mw
.
centralNotice
.
registerCampaignMixin
(
mixin
);
mockChoiceDataForRecordImpressionCall
(
choiceDataAllCampaignsFail
);
}
);
QUnit
.
test
(
'getBannerUrl'
,
(
assert
)
=>
{
const
host
=
location
.
host
;
const
protocol
=
location
.
protocol
;
[
[
'https://meta.wikimedia.org/wiki/Special:BannerLoader'
,
{
banner
:
'b1'
,
uselang
:
'fr'
,
debug
:
false
},
'https://meta.wikimedia.org/wiki/Special:BannerLoader?banner=b1&uselang=fr&debug=false'
],
[
'//meta.wikimedia.org/wiki/Special:BannerLoader'
,
{
banner
:
'b1'
,
uselang
:
'fr'
,
debug
:
false
},
`
${
protocol
}
//meta.wikimedia.org/wiki/Special:BannerLoader?banner=b1&uselang=fr&debug=false`
],
[
'https://mediawiki.org/w/index.php?title=Special:BannerLoader'
,
{
banner
:
'b2'
,
uselang
:
'es'
,
debug
:
false
},
'https://mediawiki.org/w/index.php?title=Special:BannerLoader&banner=b2&uselang=es&debug=false'
],
[
'/wiki/Special:BannerLoader'
,
{
banner
:
'b1'
,
uselang
:
'fr'
,
debug
:
false
},
`
${
protocol
}
//
${
host
}
/wiki/Special:BannerLoader?banner=b1&uselang=fr&debug=false`
],
[
'https://meta.wikimedia.org/wiki/Special:BannerLoader'
,
{
banner
:
'z + a!\'(*)=b,קי'
,
uselang
:
'qqq'
,
debug
:
false
},
'https://meta.wikimedia.org/wiki/Special:BannerLoader?banner=z+%2B+a%21%27%28%2A%29%3Db%2C%D7%A7%D7%99&uselang=qqq&debug=false'
]
].
forEach
(
(
[
path
,
data
,
expected
]
)
=>
{
const
url
=
mw
.
centralNotice
.
test
.
getBannerUrl
(
path
,
data
);
assert
.
strictEqual
(
url
.
toString
(),
expected
);
}
);
}
);
}()
);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Oct 5 2026, 01:37 (5 w, 6 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
f7/43/2c707423986b75de763f30b03a7e
Default Alt Text
index.tests.js (25 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment