Merge branch 'converse-omemo'
This commit is contained in:
commit
11cc41d3a3
@ -9,12 +9,13 @@
|
||||
"plugins": ["lodash"],
|
||||
"extends": ["eslint:recommended", "plugin:lodash/canonical"],
|
||||
"globals": {
|
||||
"Uint8Array": true,
|
||||
"Promise": true,
|
||||
"converse": true,
|
||||
"window": true,
|
||||
"sinon": true,
|
||||
"define": true,
|
||||
"require": true
|
||||
"require": true,
|
||||
"sinon": true,
|
||||
"window": true
|
||||
},
|
||||
"rules": {
|
||||
"lodash/prefer-lodash-method": [2, {
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,6 +12,7 @@
|
||||
.idea
|
||||
.su?
|
||||
builds/*
|
||||
3rdparty/libsignal-protocol-javascript/
|
||||
*.map
|
||||
dist/converse-no-dependencies-es2015.js
|
||||
|
||||
|
3295
3rdparty/bytebuffer.js
vendored
Normal file
3295
3rdparty/bytebuffer.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1207
3rdparty/long.js
vendored
Normal file
1207
3rdparty/long.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5222
3rdparty/protobuf.js
vendored
Normal file
5222
3rdparty/protobuf.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
443
css/converse.css
443
css/converse.css
@ -2346,7 +2346,7 @@
|
||||
--primary: #387592;
|
||||
--secondary: #6c757d;
|
||||
--success: #3AA569;
|
||||
--info: #17a2b8;
|
||||
--info: #3AA569;
|
||||
--warning: #ffc107;
|
||||
--danger: #E77051;
|
||||
--light: #f8f9fa;
|
||||
@ -3594,24 +3594,24 @@
|
||||
box-shadow: 0 0 0 0.2rem rgba(58, 165, 105, 0.5); }
|
||||
#conversejs .btn-info {
|
||||
color: #fff;
|
||||
background-color: #17a2b8;
|
||||
border-color: #17a2b8; }
|
||||
background-color: #3AA569;
|
||||
border-color: #3AA569; }
|
||||
#conversejs .btn-info:hover {
|
||||
color: #fff;
|
||||
background-color: #138496;
|
||||
border-color: #117a8b; }
|
||||
background-color: #308957;
|
||||
border-color: #2d7f51; }
|
||||
#conversejs .btn-info:focus, #conversejs .btn-info.focus {
|
||||
box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); }
|
||||
box-shadow: 0 0 0 0.2rem rgba(58, 165, 105, 0.5); }
|
||||
#conversejs .btn-info.disabled, #conversejs .btn-info:disabled {
|
||||
color: #fff;
|
||||
background-color: #17a2b8;
|
||||
border-color: #17a2b8; }
|
||||
background-color: #3AA569;
|
||||
border-color: #3AA569; }
|
||||
#conversejs .btn-info:not(:disabled):not(.disabled):active, #conversejs .btn-info:not(:disabled):not(.disabled).active, .show > #conversejs .btn-info.dropdown-toggle {
|
||||
color: #fff;
|
||||
background-color: #117a8b;
|
||||
border-color: #10707f; }
|
||||
background-color: #2d7f51;
|
||||
border-color: #29764b; }
|
||||
#conversejs .btn-info:not(:disabled):not(.disabled):active:focus, #conversejs .btn-info:not(:disabled):not(.disabled).active:focus, .show > #conversejs .btn-info.dropdown-toggle:focus {
|
||||
box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); }
|
||||
box-shadow: 0 0 0 0.2rem rgba(58, 165, 105, 0.5); }
|
||||
#conversejs .btn-warning {
|
||||
color: #212529;
|
||||
background-color: #ffc107;
|
||||
@ -3753,25 +3753,25 @@
|
||||
#conversejs .btn-outline-success:not(:disabled):not(.disabled):active:focus, #conversejs .btn-outline-success:not(:disabled):not(.disabled).active:focus, .show > #conversejs .btn-outline-success.dropdown-toggle:focus {
|
||||
box-shadow: 0 0 0 0.2rem rgba(58, 165, 105, 0.5); }
|
||||
#conversejs .btn-outline-info {
|
||||
color: #17a2b8;
|
||||
color: #3AA569;
|
||||
background-color: transparent;
|
||||
background-image: none;
|
||||
border-color: #17a2b8; }
|
||||
border-color: #3AA569; }
|
||||
#conversejs .btn-outline-info:hover {
|
||||
color: #fff;
|
||||
background-color: #17a2b8;
|
||||
border-color: #17a2b8; }
|
||||
background-color: #3AA569;
|
||||
border-color: #3AA569; }
|
||||
#conversejs .btn-outline-info:focus, #conversejs .btn-outline-info.focus {
|
||||
box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); }
|
||||
box-shadow: 0 0 0 0.2rem rgba(58, 165, 105, 0.5); }
|
||||
#conversejs .btn-outline-info.disabled, #conversejs .btn-outline-info:disabled {
|
||||
color: #17a2b8;
|
||||
color: #3AA569;
|
||||
background-color: transparent; }
|
||||
#conversejs .btn-outline-info:not(:disabled):not(.disabled):active, #conversejs .btn-outline-info:not(:disabled):not(.disabled).active, .show > #conversejs .btn-outline-info.dropdown-toggle {
|
||||
color: #fff;
|
||||
background-color: #17a2b8;
|
||||
border-color: #17a2b8; }
|
||||
background-color: #3AA569;
|
||||
border-color: #3AA569; }
|
||||
#conversejs .btn-outline-info:not(:disabled):not(.disabled):active:focus, #conversejs .btn-outline-info:not(:disabled):not(.disabled).active:focus, .show > #conversejs .btn-outline-info.dropdown-toggle:focus {
|
||||
box-shadow: 0 0 0 0.2rem rgba(23, 162, 184, 0.5); }
|
||||
box-shadow: 0 0 0 0.2rem rgba(58, 165, 105, 0.5); }
|
||||
#conversejs .btn-outline-warning {
|
||||
color: #ffc107;
|
||||
background-color: transparent;
|
||||
@ -4371,6 +4371,321 @@
|
||||
background-color: #e9ecef;
|
||||
border-left: 1px solid #ced4da;
|
||||
border-radius: 0 0.25rem 0.25rem 0; }
|
||||
#conversejs .nav {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding-left: 0;
|
||||
margin-bottom: 0;
|
||||
list-style: none; }
|
||||
#conversejs .nav-link {
|
||||
display: block;
|
||||
padding: 0.5rem 1rem; }
|
||||
#conversejs .nav-link:hover, #conversejs .nav-link:focus {
|
||||
text-decoration: none; }
|
||||
#conversejs .nav-link.disabled {
|
||||
color: #6c757d; }
|
||||
#conversejs .nav-tabs {
|
||||
border-bottom: 1px solid #dee2e6; }
|
||||
#conversejs .nav-tabs .nav-item {
|
||||
margin-bottom: -1px; }
|
||||
#conversejs .nav-tabs .nav-link {
|
||||
border: 1px solid transparent;
|
||||
border-top-left-radius: 0.25rem;
|
||||
border-top-right-radius: 0.25rem; }
|
||||
#conversejs .nav-tabs .nav-link:hover, #conversejs .nav-tabs .nav-link:focus {
|
||||
border-color: #e9ecef #e9ecef #dee2e6; }
|
||||
#conversejs .nav-tabs .nav-link.disabled {
|
||||
color: #6c757d;
|
||||
background-color: transparent;
|
||||
border-color: transparent; }
|
||||
#conversejs .nav-tabs .nav-link.active,
|
||||
#conversejs .nav-tabs .nav-item.show .nav-link {
|
||||
color: #495057;
|
||||
background-color: #fff;
|
||||
border-color: #dee2e6 #dee2e6 #fff; }
|
||||
#conversejs .nav-tabs .dropdown-menu {
|
||||
margin-top: -1px;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0; }
|
||||
#conversejs .nav-pills .nav-link {
|
||||
border-radius: 0.25rem; }
|
||||
#conversejs .nav-pills .nav-link.active,
|
||||
#conversejs .nav-pills .show > .nav-link {
|
||||
color: #fff;
|
||||
background-color: #387592; }
|
||||
#conversejs .nav-fill .nav-item {
|
||||
flex: 1 1 auto;
|
||||
text-align: center; }
|
||||
#conversejs .nav-justified .nav-item {
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
text-align: center; }
|
||||
#conversejs .tab-content > .tab-pane {
|
||||
display: none; }
|
||||
#conversejs .tab-content > .active {
|
||||
display: block; }
|
||||
#conversejs .navbar {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.5rem 1rem; }
|
||||
#conversejs .navbar > .container,
|
||||
#conversejs .navbar > .container-fluid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between; }
|
||||
#conversejs .navbar-brand {
|
||||
display: inline-block;
|
||||
padding-top: 0.3125rem;
|
||||
padding-bottom: 0.3125rem;
|
||||
margin-right: 1rem;
|
||||
font-size: 1.25rem;
|
||||
line-height: inherit;
|
||||
white-space: nowrap; }
|
||||
#conversejs .navbar-brand:hover, #conversejs .navbar-brand:focus {
|
||||
text-decoration: none; }
|
||||
#conversejs .navbar-nav {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-left: 0;
|
||||
margin-bottom: 0;
|
||||
list-style: none; }
|
||||
#conversejs .navbar-nav .nav-link {
|
||||
padding-right: 0;
|
||||
padding-left: 0; }
|
||||
#conversejs .navbar-nav .dropdown-menu {
|
||||
position: static;
|
||||
float: none; }
|
||||
#conversejs .navbar-text {
|
||||
display: inline-block;
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem; }
|
||||
#conversejs .navbar-collapse {
|
||||
flex-basis: 100%;
|
||||
flex-grow: 1;
|
||||
align-items: center; }
|
||||
#conversejs .navbar-toggler {
|
||||
padding: 0.25rem 0.75rem;
|
||||
font-size: 1.25rem;
|
||||
line-height: 1;
|
||||
background-color: transparent;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0.25rem; }
|
||||
#conversejs .navbar-toggler:hover, #conversejs .navbar-toggler:focus {
|
||||
text-decoration: none; }
|
||||
#conversejs .navbar-toggler:not(:disabled):not(.disabled) {
|
||||
cursor: pointer; }
|
||||
#conversejs .navbar-toggler-icon {
|
||||
display: inline-block;
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
vertical-align: middle;
|
||||
content: "";
|
||||
background: no-repeat center center;
|
||||
background-size: 100% 100%; }
|
||||
@media (max-width: 575.98px) {
|
||||
#conversejs .navbar-expand-sm > .container,
|
||||
#conversejs .navbar-expand-sm > .container-fluid {
|
||||
padding-right: 0;
|
||||
padding-left: 0; } }
|
||||
@media (min-width: 576px) {
|
||||
#conversejs .navbar-expand-sm {
|
||||
flex-flow: row nowrap;
|
||||
justify-content: flex-start; }
|
||||
#conversejs .navbar-expand-sm .navbar-nav {
|
||||
flex-direction: row; }
|
||||
#conversejs .navbar-expand-sm .navbar-nav .dropdown-menu {
|
||||
position: absolute; }
|
||||
#conversejs .navbar-expand-sm .navbar-nav .dropdown-menu-right {
|
||||
right: 0;
|
||||
left: auto; }
|
||||
#conversejs .navbar-expand-sm .navbar-nav .nav-link {
|
||||
padding-right: 0.5rem;
|
||||
padding-left: 0.5rem; }
|
||||
#conversejs .navbar-expand-sm > .container,
|
||||
#conversejs .navbar-expand-sm > .container-fluid {
|
||||
flex-wrap: nowrap; }
|
||||
#conversejs .navbar-expand-sm .navbar-collapse {
|
||||
display: flex !important;
|
||||
flex-basis: auto; }
|
||||
#conversejs .navbar-expand-sm .navbar-toggler {
|
||||
display: none; }
|
||||
#conversejs .navbar-expand-sm .dropup .dropdown-menu {
|
||||
top: auto;
|
||||
bottom: 100%; } }
|
||||
@media (max-width: 767.98px) {
|
||||
#conversejs .navbar-expand-md > .container,
|
||||
#conversejs .navbar-expand-md > .container-fluid {
|
||||
padding-right: 0;
|
||||
padding-left: 0; } }
|
||||
@media (min-width: 768px) {
|
||||
#conversejs .navbar-expand-md {
|
||||
flex-flow: row nowrap;
|
||||
justify-content: flex-start; }
|
||||
#conversejs .navbar-expand-md .navbar-nav {
|
||||
flex-direction: row; }
|
||||
#conversejs .navbar-expand-md .navbar-nav .dropdown-menu {
|
||||
position: absolute; }
|
||||
#conversejs .navbar-expand-md .navbar-nav .dropdown-menu-right {
|
||||
right: 0;
|
||||
left: auto; }
|
||||
#conversejs .navbar-expand-md .navbar-nav .nav-link {
|
||||
padding-right: 0.5rem;
|
||||
padding-left: 0.5rem; }
|
||||
#conversejs .navbar-expand-md > .container,
|
||||
#conversejs .navbar-expand-md > .container-fluid {
|
||||
flex-wrap: nowrap; }
|
||||
#conversejs .navbar-expand-md .navbar-collapse {
|
||||
display: flex !important;
|
||||
flex-basis: auto; }
|
||||
#conversejs .navbar-expand-md .navbar-toggler {
|
||||
display: none; }
|
||||
#conversejs .navbar-expand-md .dropup .dropdown-menu {
|
||||
top: auto;
|
||||
bottom: 100%; } }
|
||||
@media (max-width: 991.98px) {
|
||||
#conversejs .navbar-expand-lg > .container,
|
||||
#conversejs .navbar-expand-lg > .container-fluid {
|
||||
padding-right: 0;
|
||||
padding-left: 0; } }
|
||||
@media (min-width: 992px) {
|
||||
#conversejs .navbar-expand-lg {
|
||||
flex-flow: row nowrap;
|
||||
justify-content: flex-start; }
|
||||
#conversejs .navbar-expand-lg .navbar-nav {
|
||||
flex-direction: row; }
|
||||
#conversejs .navbar-expand-lg .navbar-nav .dropdown-menu {
|
||||
position: absolute; }
|
||||
#conversejs .navbar-expand-lg .navbar-nav .dropdown-menu-right {
|
||||
right: 0;
|
||||
left: auto; }
|
||||
#conversejs .navbar-expand-lg .navbar-nav .nav-link {
|
||||
padding-right: 0.5rem;
|
||||
padding-left: 0.5rem; }
|
||||
#conversejs .navbar-expand-lg > .container,
|
||||
#conversejs .navbar-expand-lg > .container-fluid {
|
||||
flex-wrap: nowrap; }
|
||||
#conversejs .navbar-expand-lg .navbar-collapse {
|
||||
display: flex !important;
|
||||
flex-basis: auto; }
|
||||
#conversejs .navbar-expand-lg .navbar-toggler {
|
||||
display: none; }
|
||||
#conversejs .navbar-expand-lg .dropup .dropdown-menu {
|
||||
top: auto;
|
||||
bottom: 100%; } }
|
||||
@media (max-width: 1199.98px) {
|
||||
#conversejs .navbar-expand-xl > .container,
|
||||
#conversejs .navbar-expand-xl > .container-fluid {
|
||||
padding-right: 0;
|
||||
padding-left: 0; } }
|
||||
@media (min-width: 1200px) {
|
||||
#conversejs .navbar-expand-xl {
|
||||
flex-flow: row nowrap;
|
||||
justify-content: flex-start; }
|
||||
#conversejs .navbar-expand-xl .navbar-nav {
|
||||
flex-direction: row; }
|
||||
#conversejs .navbar-expand-xl .navbar-nav .dropdown-menu {
|
||||
position: absolute; }
|
||||
#conversejs .navbar-expand-xl .navbar-nav .dropdown-menu-right {
|
||||
right: 0;
|
||||
left: auto; }
|
||||
#conversejs .navbar-expand-xl .navbar-nav .nav-link {
|
||||
padding-right: 0.5rem;
|
||||
padding-left: 0.5rem; }
|
||||
#conversejs .navbar-expand-xl > .container,
|
||||
#conversejs .navbar-expand-xl > .container-fluid {
|
||||
flex-wrap: nowrap; }
|
||||
#conversejs .navbar-expand-xl .navbar-collapse {
|
||||
display: flex !important;
|
||||
flex-basis: auto; }
|
||||
#conversejs .navbar-expand-xl .navbar-toggler {
|
||||
display: none; }
|
||||
#conversejs .navbar-expand-xl .dropup .dropdown-menu {
|
||||
top: auto;
|
||||
bottom: 100%; } }
|
||||
#conversejs .navbar-expand {
|
||||
flex-flow: row nowrap;
|
||||
justify-content: flex-start; }
|
||||
#conversejs .navbar-expand > .container,
|
||||
#conversejs .navbar-expand > .container-fluid {
|
||||
padding-right: 0;
|
||||
padding-left: 0; }
|
||||
#conversejs .navbar-expand .navbar-nav {
|
||||
flex-direction: row; }
|
||||
#conversejs .navbar-expand .navbar-nav .dropdown-menu {
|
||||
position: absolute; }
|
||||
#conversejs .navbar-expand .navbar-nav .dropdown-menu-right {
|
||||
right: 0;
|
||||
left: auto; }
|
||||
#conversejs .navbar-expand .navbar-nav .nav-link {
|
||||
padding-right: 0.5rem;
|
||||
padding-left: 0.5rem; }
|
||||
#conversejs .navbar-expand > .container,
|
||||
#conversejs .navbar-expand > .container-fluid {
|
||||
flex-wrap: nowrap; }
|
||||
#conversejs .navbar-expand .navbar-collapse {
|
||||
display: flex !important;
|
||||
flex-basis: auto; }
|
||||
#conversejs .navbar-expand .navbar-toggler {
|
||||
display: none; }
|
||||
#conversejs .navbar-expand .dropup .dropdown-menu {
|
||||
top: auto;
|
||||
bottom: 100%; }
|
||||
#conversejs .navbar-light .navbar-brand {
|
||||
color: rgba(0, 0, 0, 0.9); }
|
||||
#conversejs .navbar-light .navbar-brand:hover, #conversejs .navbar-light .navbar-brand:focus {
|
||||
color: rgba(0, 0, 0, 0.9); }
|
||||
#conversejs .navbar-light .navbar-nav .nav-link {
|
||||
color: rgba(0, 0, 0, 0.5); }
|
||||
#conversejs .navbar-light .navbar-nav .nav-link:hover, #conversejs .navbar-light .navbar-nav .nav-link:focus {
|
||||
color: rgba(0, 0, 0, 0.7); }
|
||||
#conversejs .navbar-light .navbar-nav .nav-link.disabled {
|
||||
color: rgba(0, 0, 0, 0.3); }
|
||||
#conversejs .navbar-light .navbar-nav .show > .nav-link,
|
||||
#conversejs .navbar-light .navbar-nav .active > .nav-link,
|
||||
#conversejs .navbar-light .navbar-nav .nav-link.show,
|
||||
#conversejs .navbar-light .navbar-nav .nav-link.active {
|
||||
color: rgba(0, 0, 0, 0.9); }
|
||||
#conversejs .navbar-light .navbar-toggler {
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
border-color: rgba(0, 0, 0, 0.1); }
|
||||
#conversejs .navbar-light .navbar-toggler-icon {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"); }
|
||||
#conversejs .navbar-light .navbar-text {
|
||||
color: rgba(0, 0, 0, 0.5); }
|
||||
#conversejs .navbar-light .navbar-text a {
|
||||
color: rgba(0, 0, 0, 0.9); }
|
||||
#conversejs .navbar-light .navbar-text a:hover, #conversejs .navbar-light .navbar-text a:focus {
|
||||
color: rgba(0, 0, 0, 0.9); }
|
||||
#conversejs .navbar-dark .navbar-brand {
|
||||
color: #fff; }
|
||||
#conversejs .navbar-dark .navbar-brand:hover, #conversejs .navbar-dark .navbar-brand:focus {
|
||||
color: #fff; }
|
||||
#conversejs .navbar-dark .navbar-nav .nav-link {
|
||||
color: rgba(255, 255, 255, 0.5); }
|
||||
#conversejs .navbar-dark .navbar-nav .nav-link:hover, #conversejs .navbar-dark .navbar-nav .nav-link:focus {
|
||||
color: rgba(255, 255, 255, 0.75); }
|
||||
#conversejs .navbar-dark .navbar-nav .nav-link.disabled {
|
||||
color: rgba(255, 255, 255, 0.25); }
|
||||
#conversejs .navbar-dark .navbar-nav .show > .nav-link,
|
||||
#conversejs .navbar-dark .navbar-nav .active > .nav-link,
|
||||
#conversejs .navbar-dark .navbar-nav .nav-link.show,
|
||||
#conversejs .navbar-dark .navbar-nav .nav-link.active {
|
||||
color: #fff; }
|
||||
#conversejs .navbar-dark .navbar-toggler {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
border-color: rgba(255, 255, 255, 0.1); }
|
||||
#conversejs .navbar-dark .navbar-toggler-icon {
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E"); }
|
||||
#conversejs .navbar-dark .navbar-text {
|
||||
color: rgba(255, 255, 255, 0.5); }
|
||||
#conversejs .navbar-dark .navbar-text a {
|
||||
color: #fff; }
|
||||
#conversejs .navbar-dark .navbar-text a:hover, #conversejs .navbar-dark .navbar-text a:focus {
|
||||
color: #fff; }
|
||||
#conversejs .card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
@ -4582,11 +4897,11 @@
|
||||
background-color: #2d7f51; }
|
||||
#conversejs .badge-info {
|
||||
color: #fff;
|
||||
background-color: #17a2b8; }
|
||||
background-color: #3AA569; }
|
||||
#conversejs .badge-info[href]:hover, #conversejs .badge-info[href]:focus {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
background-color: #117a8b; }
|
||||
background-color: #2d7f51; }
|
||||
#conversejs .badge-warning {
|
||||
color: #212529;
|
||||
background-color: #ffc107; }
|
||||
@ -4658,13 +4973,13 @@
|
||||
#conversejs .alert-success .alert-link {
|
||||
color: #11301f; }
|
||||
#conversejs .alert-info {
|
||||
color: #0c5460;
|
||||
background-color: #d1ecf1;
|
||||
border-color: #bee5eb; }
|
||||
color: #1e5637;
|
||||
background-color: #d8ede1;
|
||||
border-color: #c8e6d5; }
|
||||
#conversejs .alert-info hr {
|
||||
border-top-color: #abdde5; }
|
||||
border-top-color: #b6dec8; }
|
||||
#conversejs .alert-info .alert-link {
|
||||
color: #062c33; }
|
||||
color: #11301f; }
|
||||
#conversejs .alert-warning {
|
||||
color: #856404;
|
||||
background-color: #fff3cd;
|
||||
@ -4782,15 +5097,15 @@
|
||||
background-color: #1e5637;
|
||||
border-color: #1e5637; }
|
||||
#conversejs .list-group-item-info {
|
||||
color: #0c5460;
|
||||
background-color: #bee5eb; }
|
||||
color: #1e5637;
|
||||
background-color: #c8e6d5; }
|
||||
#conversejs .list-group-item-info.list-group-item-action:hover, #conversejs .list-group-item-info.list-group-item-action:focus {
|
||||
color: #0c5460;
|
||||
background-color: #abdde5; }
|
||||
color: #1e5637;
|
||||
background-color: #b6dec8; }
|
||||
#conversejs .list-group-item-info.list-group-item-action.active {
|
||||
color: #fff;
|
||||
background-color: #0c5460;
|
||||
border-color: #0c5460; }
|
||||
background-color: #1e5637;
|
||||
border-color: #1e5637; }
|
||||
#conversejs .list-group-item-warning {
|
||||
color: #856404;
|
||||
background-color: #ffeeba; }
|
||||
@ -5179,11 +5494,11 @@
|
||||
#conversejs button.bg-success:focus {
|
||||
background-color: #2d7f51 !important; }
|
||||
#conversejs .bg-info {
|
||||
background-color: #17a2b8 !important; }
|
||||
background-color: #3AA569 !important; }
|
||||
#conversejs a.bg-info:hover, #conversejs a.bg-info:focus,
|
||||
#conversejs button.bg-info:hover,
|
||||
#conversejs button.bg-info:focus {
|
||||
background-color: #117a8b !important; }
|
||||
background-color: #2d7f51 !important; }
|
||||
#conversejs .bg-warning {
|
||||
background-color: #ffc107 !important; }
|
||||
#conversejs a.bg-warning:hover, #conversejs a.bg-warning:focus,
|
||||
@ -5239,7 +5554,7 @@
|
||||
#conversejs .border-success {
|
||||
border-color: #3AA569 !important; }
|
||||
#conversejs .border-info {
|
||||
border-color: #17a2b8 !important; }
|
||||
border-color: #3AA569 !important; }
|
||||
#conversejs .border-warning {
|
||||
border-color: #ffc107 !important; }
|
||||
#conversejs .border-danger {
|
||||
@ -6792,9 +7107,9 @@
|
||||
#conversejs a.text-success:hover, #conversejs a.text-success:focus {
|
||||
color: #2d7f51 !important; }
|
||||
#conversejs .text-info {
|
||||
color: #17a2b8 !important; }
|
||||
color: #3AA569 !important; }
|
||||
#conversejs a.text-info:hover, #conversejs a.text-info:focus {
|
||||
color: #117a8b !important; }
|
||||
color: #2d7f51 !important; }
|
||||
#conversejs .text-warning {
|
||||
color: #ffc107 !important; }
|
||||
#conversejs a.text-warning:hover, #conversejs a.text-warning:focus {
|
||||
@ -6868,6 +7183,8 @@ body.reset {
|
||||
font-size: 14px;
|
||||
direction: ltr;
|
||||
z-index: 1031; }
|
||||
#conversejs .nopadding {
|
||||
padding: 0 !important; }
|
||||
#conversejs.converse-overlayed > .row {
|
||||
flex-direction: row-reverse; }
|
||||
#conversejs.converse-fullscreen .converse-chatboxes, #conversejs.converse-mobile .converse-chatboxes {
|
||||
@ -7213,8 +7530,9 @@ body.reset {
|
||||
@media screen and (max-height: 450px) {
|
||||
#conversejs {
|
||||
left: 0; } }
|
||||
#conversejs form .form-group {
|
||||
margin-bottom: 2em; }
|
||||
#conversejs .btn--small {
|
||||
font-size: 80%;
|
||||
font-weight: normal; }
|
||||
#conversejs form .form-check-label {
|
||||
margin-top: 0.3rem; }
|
||||
#conversejs form .form-control::-webkit-input-placeholder {
|
||||
@ -7286,12 +7604,11 @@ body.reset {
|
||||
color: #79a5ba; }
|
||||
#conversejs form.converse-form .text-muted.error {
|
||||
color: #A53214; }
|
||||
#conversejs form.converse-form--modal {
|
||||
padding-bottom: 0; }
|
||||
#conversejs form.converse-centered-form {
|
||||
text-align: center; }
|
||||
|
||||
#conversejs #user-profile-modal label {
|
||||
font-weight: bold; }
|
||||
|
||||
#conversejs .chatbox-navback {
|
||||
display: none; }
|
||||
#conversejs .flyout {
|
||||
@ -7337,6 +7654,13 @@ body.reset {
|
||||
font-size: 80%; }
|
||||
#conversejs .chat-head .chatbox-buttons {
|
||||
flex-direction: row-reverse;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
min-height: 1px;
|
||||
padding-right: 15px;
|
||||
padding-left: 15px;
|
||||
flex: 0 0 25%;
|
||||
max-width: 25%;
|
||||
padding: 0; }
|
||||
#conversejs .chat-head .user-custom-message {
|
||||
color: #e7f7ee;
|
||||
@ -7843,10 +8167,6 @@ body.reset {
|
||||
padding: 0.3em 0;
|
||||
clear: left;
|
||||
width: 100%; }
|
||||
#conversejs #converse-modals .set-xmpp-status {
|
||||
margin: 1em; }
|
||||
#conversejs #converse-modals .set-xmpp-status .custom-control-label {
|
||||
margin-top: 0.25em; }
|
||||
#conversejs #controlbox {
|
||||
margin-right: 1.5em; }
|
||||
#conversejs #controlbox .box-flyout {
|
||||
@ -8247,6 +8567,35 @@ body.reset {
|
||||
|
||||
#conversejs.converse-overlayed .converse-chatboxes .chatbox .box-flyout {
|
||||
margin-left: 30px; } }
|
||||
#conversejs #converse-modals .set-xmpp-status {
|
||||
margin: 1em; }
|
||||
#conversejs #converse-modals .set-xmpp-status .custom-control-label {
|
||||
margin-top: 0.25em; }
|
||||
#conversejs #converse-modals #omemo-tabpanel {
|
||||
margin-top: 1em; }
|
||||
#conversejs #converse-modals .btn {
|
||||
font-weight: normal; }
|
||||
#conversejs #converse-modals #user-profile-modal .profile-form label {
|
||||
font-weight: bold; }
|
||||
#conversejs #converse-modals #user-profile-modal .fingerprint-removal label {
|
||||
display: flex;
|
||||
padding: 0.75rem 1.25rem; }
|
||||
#conversejs #converse-modals #user-profile-modal .list-group-item {
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
font-size: 95%; }
|
||||
#conversejs #converse-modals #user-profile-modal .list-group-item input[type="checkbox"] {
|
||||
margin-right: 1em; }
|
||||
#conversejs #converse-modals .fingerprints {
|
||||
width: 100%;
|
||||
margin-bottom: 1em; }
|
||||
#conversejs #converse-modals .fingerprint-trust {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 95%; }
|
||||
#conversejs #converse-modals .fingerprint-trust .fingerprint {
|
||||
margin-left: 1em; }
|
||||
|
||||
#conversejs #converse-roster {
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
|
1
dev.html
1
dev.html
@ -11,6 +11,7 @@
|
||||
<link rel="shortcut icon" type="image/ico" href="css/images/favicon.ico"/>
|
||||
<link type="text/css" rel="stylesheet" media="screen" href="css/fullpage.css" />
|
||||
<link type="text/css" rel="stylesheet" media="screen" href="css/converse.css" />
|
||||
<script src="3rdparty/libsignal-protocol-javascript/dist/libsignal-protocol.js"></script>
|
||||
<script src="dist/converse.js"></script>
|
||||
</head>
|
||||
|
||||
|
8042
dist/converse.js
vendored
8042
dist/converse.js
vendored
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: Converse.js 0.4\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-07-22 11:17+0200\n"
|
||||
"PO-Revision-Date: 2018-07-22 12:12+0200\n"
|
||||
"PO-Revision-Date: 2018-07-22 15:37+0200\n"
|
||||
"Last-Translator: JC Brand <jc@opkode.com>\n"
|
||||
"Language-Team: Afrikaans <https://hosted.weblate.org/projects/conversejs/"
|
||||
"translations/af/>\n"
|
||||
|
@ -134,12 +134,33 @@
|
||||
<span class="chat-msg__heading">
|
||||
<span class="chat-msg__author">Juliet Capulet</span>
|
||||
<span class="chat-msg__time">15:31</span>
|
||||
<span class="fa fa-lock"></span>
|
||||
</span>
|
||||
<div class="chat-msg__body">
|
||||
<div class="chat-msg__message">
|
||||
<span class="chat-msg__text">
|
||||
O Romeo, Romeo! wherefore art thou Romeo?
|
||||
Deny thy father and refuse thy name;
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-msg__actions">
|
||||
<button class="chat-msg__action fa fa-pencil" title="Edit this message"> </button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="message chat-msg chat-msg--followup">
|
||||
<canvas height="36" width="36" class="avatar chat-msg__avatar"></canvas>
|
||||
<div class="chat-msg__content">
|
||||
<span class="chat-msg__heading">
|
||||
<span class="chat-msg__author">Juliet Capulet</span>
|
||||
<span class="chat-msg__time">15:31</span>
|
||||
<span class="fa fa-lock"></span>
|
||||
</span>
|
||||
<div class="chat-msg__body">
|
||||
<div class="chat-msg__message">
|
||||
<span class="chat-msg__text">
|
||||
Or, if thou wilt not, be but sworn my love,
|
||||
And I'll no longer be a Capulet.
|
||||
</span>
|
||||
|
5764
package-lock.json
generated
5764
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -46,6 +46,7 @@
|
||||
"bootstrap": "^4.0.0",
|
||||
"bootstrap.native": "^2.0.23",
|
||||
"bourbon": "^4.3.2",
|
||||
"bytebuffer": "^3.5.5",
|
||||
"clean-css-cli": "^4.0.10",
|
||||
"emojione": "^3.0.3",
|
||||
"es6-promise": "^4.1.0",
|
||||
@ -65,6 +66,7 @@
|
||||
"jshint": "^2.9.4",
|
||||
"lodash": "4.17.4",
|
||||
"lodash-template-loader": "^2.0.0",
|
||||
"long": "^3.1.0",
|
||||
"lodash-template-webpack-loader": "jcbrand/lodash-template-webpack-loader",
|
||||
"minimist": "^1.2.0",
|
||||
"moment": "~> 2.19.3 ",
|
||||
@ -72,6 +74,7 @@
|
||||
"otr": "0.2.16",
|
||||
"pluggable.js": "2.0.0",
|
||||
"po2json": "^0.4.4",
|
||||
"protobufjs": "5.0.1",
|
||||
"requirejs": "2.3.5",
|
||||
"run-headless-chromium": "^0.1.1",
|
||||
"sinon": "^2.1.0",
|
||||
|
@ -61,6 +61,8 @@
|
||||
}
|
||||
.chatbox-buttons {
|
||||
flex-direction: row-reverse;
|
||||
@include make-col-ready();
|
||||
@include make-col(3);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
@ -60,15 +60,6 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#converse-modals {
|
||||
.set-xmpp-status {
|
||||
margin: 1em;
|
||||
.custom-control-label {
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#controlbox {
|
||||
.box-flyout {
|
||||
background-color: white;
|
||||
|
@ -61,6 +61,10 @@ body.reset {
|
||||
direction: ltr;
|
||||
z-index: 1031; // One more than bootstrap navbar
|
||||
|
||||
.nopadding {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
&.converse-overlayed {
|
||||
> .row {
|
||||
flex-direction: row-reverse;
|
||||
|
@ -1,9 +1,10 @@
|
||||
#conversejs {
|
||||
form {
|
||||
.form-group {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
.btn--small {
|
||||
font-size: 80%;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
form {
|
||||
.form-check-label {
|
||||
margin-top: $form-check-input-margin-y;
|
||||
}
|
||||
@ -103,6 +104,11 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.converse-form--modal {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
&.converse-centered-form {
|
||||
text-align: center;
|
||||
}
|
||||
|
56
sass/_modal.scss
Normal file
56
sass/_modal.scss
Normal file
@ -0,0 +1,56 @@
|
||||
#conversejs {
|
||||
#converse-modals {
|
||||
.set-xmpp-status {
|
||||
margin: 1em;
|
||||
.custom-control-label {
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
#omemo-tabpanel {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.btn {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#user-profile-modal {
|
||||
.profile-form {
|
||||
label {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
.fingerprint-removal {
|
||||
label {
|
||||
display: flex;
|
||||
padding: 0.75rem 1.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
font-size: 95%;
|
||||
|
||||
input[type="checkbox"] {
|
||||
margin-right: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.fingerprints {
|
||||
width: 100%;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.fingerprint-trust {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 95%;
|
||||
.fingerprint {
|
||||
margin-left: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
#conversejs {
|
||||
#user-profile-modal {
|
||||
label {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
@ -34,6 +34,8 @@ $green: #3AA569;
|
||||
$dark-green: #1E9652;
|
||||
$darkest-green: #0E763B;
|
||||
|
||||
$info: $green !default;
|
||||
|
||||
$lightest-green: #E7FBF0;
|
||||
$light-green: #5CBC86;
|
||||
$green: #3AA569;
|
||||
|
@ -26,6 +26,8 @@
|
||||
@import "bootstrap/scss/button-group";
|
||||
@import "bootstrap/scss/input-group";
|
||||
@import "bootstrap/scss/custom-forms";
|
||||
@import "bootstrap/scss/nav";
|
||||
@import "bootstrap/scss/navbar";
|
||||
@import "bootstrap/scss/card";
|
||||
@import "bootstrap/scss/breadcrumb";
|
||||
@import "bootstrap/scss/badge";
|
||||
@ -40,9 +42,9 @@
|
||||
}
|
||||
@import "core";
|
||||
@import "forms";
|
||||
@import "profile";
|
||||
@import "chatbox";
|
||||
@import "controlbox";
|
||||
@import "modal";
|
||||
@import "roster";
|
||||
@import "lists";
|
||||
@import "chatrooms";
|
||||
|
@ -314,52 +314,54 @@
|
||||
[{'category': 'pubsub', 'type': 'pep'}],
|
||||
['http://jabber.org/protocol/pubsub#publish-options']
|
||||
).then(function () {
|
||||
test_utils.waitUntil(function () {
|
||||
return _converse.bookmarks;
|
||||
}, 300).then(function () {
|
||||
/* The stored data is automatically pushed to all of the user's
|
||||
* connected resources.
|
||||
*
|
||||
* Publisher receives event notification
|
||||
* -------------------------------------
|
||||
* <message from='juliet@capulet.lit'
|
||||
* to='juliet@capulet.lit/balcony'
|
||||
* type='headline'
|
||||
* id='rnfoo1'>
|
||||
* <event xmlns='http://jabber.org/protocol/pubsub#event'>
|
||||
* <items node='storage:bookmarks'>
|
||||
* <item id='current'>
|
||||
* <storage xmlns='storage:bookmarks'>
|
||||
* <conference name='The Play's the Thing'
|
||||
* autojoin='true'
|
||||
* jid='theplay@conference.shakespeare.lit'>
|
||||
* <nick>JC</nick>
|
||||
* </conference>
|
||||
* </storage>
|
||||
* </item>
|
||||
* </items>
|
||||
* </event>
|
||||
* </message>
|
||||
*/
|
||||
var stanza = $msg({
|
||||
'from': 'dummy@localhost',
|
||||
'to': 'dummy@localhost/resource',
|
||||
'type': 'headline',
|
||||
'id': 'rnfoo1'
|
||||
}).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
|
||||
.c('items', {'node': 'storage:bookmarks'})
|
||||
.c('item', {'id': 'current'})
|
||||
.c('storage', {'xmlns': 'storage:bookmarks'})
|
||||
.c('conference', {'name': 'The Play's the Thing',
|
||||
'autojoin': 'true',
|
||||
'jid':'theplay@conference.shakespeare.lit'})
|
||||
.c('nick').t('JC');
|
||||
return test_utils.waitUntil(() => _converse.bookmarks);
|
||||
}).then(function () {
|
||||
// Emit here instead of mocking fetching of bookmarks.
|
||||
_converse.emit('bookmarksInitialized');
|
||||
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
expect(_converse.bookmarks.length).toBe(1);
|
||||
expect(_converse.chatboxviews.get('theplay@conference.shakespeare.lit')).not.toBeUndefined();
|
||||
done();
|
||||
});
|
||||
/* The stored data is automatically pushed to all of the user's
|
||||
* connected resources.
|
||||
*
|
||||
* Publisher receives event notification
|
||||
* -------------------------------------
|
||||
* <message from='juliet@capulet.lit'
|
||||
* to='juliet@capulet.lit/balcony'
|
||||
* type='headline'
|
||||
* id='rnfoo1'>
|
||||
* <event xmlns='http://jabber.org/protocol/pubsub#event'>
|
||||
* <items node='storage:bookmarks'>
|
||||
* <item id='current'>
|
||||
* <storage xmlns='storage:bookmarks'>
|
||||
* <conference name='The Play's the Thing'
|
||||
* autojoin='true'
|
||||
* jid='theplay@conference.shakespeare.lit'>
|
||||
* <nick>JC</nick>
|
||||
* </conference>
|
||||
* </storage>
|
||||
* </item>
|
||||
* </items>
|
||||
* </event>
|
||||
* </message>
|
||||
*/
|
||||
var stanza = $msg({
|
||||
'from': 'dummy@localhost',
|
||||
'to': 'dummy@localhost/resource',
|
||||
'type': 'headline',
|
||||
'id': 'rnfoo1'
|
||||
}).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
|
||||
.c('items', {'node': 'storage:bookmarks'})
|
||||
.c('item', {'id': 'current'})
|
||||
.c('storage', {'xmlns': 'storage:bookmarks'})
|
||||
.c('conference', {'name': 'The Play's the Thing',
|
||||
'autojoin': 'true',
|
||||
'jid':'theplay@conference.shakespeare.lit'})
|
||||
.c('nick').t('JC');
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
return test_utils.waitUntil(() => _converse.bookmarks.length);
|
||||
}).then(function () {
|
||||
expect(_converse.bookmarks.length).toBe(1);
|
||||
expect(_converse.chatboxviews.get('theplay@conference.shakespeare.lit')).not.toBeUndefined();
|
||||
done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
283
spec/chatbox.js
283
spec/chatbox.js
@ -27,10 +27,11 @@
|
||||
_converse.emit('rosterContactsFetched');
|
||||
test_utils.openControlBox();
|
||||
|
||||
let view;
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
test_utils.openChatBoxFor(_converse, contact_jid);
|
||||
test_utils.waitUntil(() => _converse.chatboxes.length == 2).then(() => {
|
||||
var view = _converse.chatboxviews.get(contact_jid);
|
||||
test_utils.openChatBoxFor(_converse, contact_jid)
|
||||
.then(() => {
|
||||
view = _converse.chatboxviews.get(contact_jid);
|
||||
test_utils.sendMessage(view, '/help');
|
||||
|
||||
const info_messages = Array.prototype.slice.call(view.el.querySelectorAll('.chat-info:not(.chat-date)'), 0);
|
||||
@ -39,13 +40,15 @@
|
||||
expect(info_messages.pop().textContent).toBe('/me: Write in the third person');
|
||||
expect(info_messages.pop().textContent).toBe('/clear: Remove messages');
|
||||
|
||||
var msg = $msg({
|
||||
const msg = $msg({
|
||||
from: contact_jid,
|
||||
to: _converse.connection.jid,
|
||||
type: 'chat',
|
||||
id: (new Date()).getTime()
|
||||
}).c('body').t('hello world').tree();
|
||||
_converse.chatboxes.onMessage(msg);
|
||||
return test_utils.waitUntil(() => view.content.querySelectorAll('.chat-msg').length);
|
||||
}).then(() => {
|
||||
expect(view.content.lastElementChild.textContent.trim().indexOf('hello world')).not.toBe(-1);
|
||||
done();
|
||||
});
|
||||
@ -618,10 +621,10 @@
|
||||
expect(view.model.get('chat_state')).toBe('inactive');
|
||||
spyOn(_converse.connection, 'send');
|
||||
view.model.maximize();
|
||||
return test_utils.waitUntil(() => view.model.get('chat_state') === 'active', 700);
|
||||
return test_utils.waitUntil(() => view.model.get('chat_state') === 'active', 1000);
|
||||
}).then(() => {
|
||||
expect(_converse.connection.send).toHaveBeenCalled();
|
||||
var calls = _.filter(_converse.connection.send.calls.all(), function (call) {
|
||||
const calls = _.filter(_converse.connection.send.calls.all(), function (call) {
|
||||
return call.args[0] instanceof Strophe.Builder;
|
||||
});
|
||||
expect(calls.length).toBe(1);
|
||||
@ -632,7 +635,7 @@
|
||||
expect($stanza.children().get(1).tagName).toBe('no-store');
|
||||
expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
|
||||
done();
|
||||
});
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
});
|
||||
|
||||
@ -741,7 +744,7 @@
|
||||
spyOn(_converse, 'log');
|
||||
recipient_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
return test_utils.openChatBoxFor(_converse, recipient_jid);
|
||||
}).then(() => {
|
||||
}).then((view) => {
|
||||
var msg = $msg({
|
||||
'from': _converse.bare_jid,
|
||||
'id': (new Date()).getTime(),
|
||||
@ -757,7 +760,8 @@
|
||||
'type': 'chat'
|
||||
}).c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
|
||||
_converse.chatboxes.onMessage(msg);
|
||||
|
||||
return test_utils.waitUntil(() => view.model.messages.length);
|
||||
}).then(() => {
|
||||
// Check that the chatbox and its view now exist
|
||||
var chatbox = _converse.chatboxes.get(recipient_jid);
|
||||
var chatboxview = _converse.chatboxviews.get(recipient_jid);
|
||||
@ -886,7 +890,7 @@
|
||||
spyOn(_converse, 'log');
|
||||
recipient_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
return test_utils.openChatBoxFor(_converse, recipient_jid);
|
||||
}).then(() => {
|
||||
}).then((view) => {
|
||||
var msg = $msg({
|
||||
'from': _converse.bare_jid,
|
||||
'id': (new Date()).getTime(),
|
||||
@ -902,7 +906,8 @@
|
||||
'type': 'chat'
|
||||
}).c('paused', {'xmlns': Strophe.NS.CHATSTATES}).tree();
|
||||
_converse.chatboxes.onMessage(msg);
|
||||
|
||||
return test_utils.waitUntil(() => view.model.messages.length);
|
||||
}).then(() => {
|
||||
// Check that the chatbox and its view now exist
|
||||
var chatbox = _converse.chatboxes.get(recipient_jid);
|
||||
var chatboxview = _converse.chatboxviews.get(recipient_jid);
|
||||
@ -936,11 +941,11 @@
|
||||
let view;
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
test_utils.openControlBox();
|
||||
test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length, 500)
|
||||
test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length, 1000)
|
||||
.then(() => test_utils.openChatBoxFor(_converse, contact_jid))
|
||||
.then(() => {
|
||||
view = _converse.chatboxviews.get(contact_jid);
|
||||
return test_utils.waitUntil(() => view.model.get('chat_state') === 'active', 500);
|
||||
return test_utils.waitUntil(() => view.model.get('chat_state') === 'active', 1000);
|
||||
}).then(() => {
|
||||
console.log('chat_state set to active');
|
||||
expect(view.model.get('chat_state')).toBe('active');
|
||||
@ -954,10 +959,9 @@
|
||||
view = _converse.chatboxviews.get(contact_jid);
|
||||
expect(view.model.get('chat_state')).toBe('composing');
|
||||
spyOn(_converse.connection, 'send');
|
||||
return test_utils.waitUntil(() => view.model.get('chat_state') === 'paused', 500);
|
||||
}).then(() => {
|
||||
return test_utils.waitUntil(() => view.model.get('chat_state') === 'inactive', 500);
|
||||
}).then(() => {
|
||||
return test_utils.waitUntil(() => view.model.get('chat_state') === 'paused', 1000);
|
||||
}).then(() => test_utils.waitUntil(() => view.model.get('chat_state') === 'inactive', 1000))
|
||||
.then(() => {
|
||||
console.log('chat_state set to inactive');
|
||||
expect(_converse.connection.send).toHaveBeenCalled();
|
||||
var calls = _.filter(_converse.connection.send.calls.all(), function (call) {
|
||||
@ -1041,17 +1045,18 @@
|
||||
_converse.emit('rosterContactsFetched');
|
||||
test_utils.openControlBox();
|
||||
const sender_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
let view;
|
||||
|
||||
// See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
|
||||
spyOn(_converse, 'emit');
|
||||
test_utils.openChatBoxFor(_converse, sender_jid)
|
||||
.then(() => {
|
||||
var view = _converse.chatboxviews.get(sender_jid);
|
||||
view = _converse.chatboxviews.get(sender_jid);
|
||||
expect(view.el.querySelectorAll('.chat-event').length).toBe(0);
|
||||
// Insert <composing> message, to also check that
|
||||
// text messages are inserted correctly with
|
||||
// temporary chat events in the chat contents.
|
||||
var msg = $msg({
|
||||
const msg = $msg({
|
||||
'to': _converse.bare_jid,
|
||||
'xmlns': 'jabber:client',
|
||||
'from': sender_jid,
|
||||
@ -1059,14 +1064,18 @@
|
||||
.c('composing', {'xmlns': Strophe.NS.CHATSTATES}).up()
|
||||
.tree();
|
||||
_converse.chatboxes.onMessage(msg);
|
||||
return test_utils.waitUntil(() => view.model.messages.length);
|
||||
}).then(() => {
|
||||
expect(view.el.querySelectorAll('.chat-state-notification').length).toBe(1);
|
||||
msg = $msg({
|
||||
const msg = $msg({
|
||||
from: sender_jid,
|
||||
to: _converse.connection.jid,
|
||||
type: 'chat',
|
||||
id: (new Date()).getTime()
|
||||
}).c('body').c('inactive', {'xmlns': Strophe.NS.CHATSTATES}).tree();
|
||||
_converse.chatboxes.onMessage(msg);
|
||||
return test_utils.waitUntil(() => (view.model.messages.length > 1));
|
||||
}).then(() => {
|
||||
expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
|
||||
expect($(view.el).find('.chat-state-notification').length).toBe(0);
|
||||
done();
|
||||
@ -1178,12 +1187,15 @@
|
||||
.c('active', {'xmlns': Strophe.NS.CHATSTATES}).tree();
|
||||
_converse.windowState = 'hidden';
|
||||
_converse.chatboxes.onMessage(msg);
|
||||
expect(_converse.incrementMsgCounter).toHaveBeenCalled();
|
||||
expect(_converse.clearMsgCounter).not.toHaveBeenCalled();
|
||||
expect(_converse.msg_counter).toBe(1);
|
||||
expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
|
||||
_converse.windowSate = previous_state;
|
||||
done();
|
||||
return test_utils.waitUntil(() => _converse.api.chats.get().length)
|
||||
.then(() => {
|
||||
expect(_converse.incrementMsgCounter).toHaveBeenCalled();
|
||||
expect(_converse.clearMsgCounter).not.toHaveBeenCalled();
|
||||
expect(_converse.msg_counter).toBe(1);
|
||||
expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
|
||||
_converse.windowSate = previous_state;
|
||||
done();
|
||||
});
|
||||
}));
|
||||
|
||||
it("is cleared when the window is focused",
|
||||
@ -1237,7 +1249,8 @@
|
||||
// initial state
|
||||
expect(_converse.msg_counter).toBe(0);
|
||||
|
||||
var message = 'This message will always increment the message counter from zero',
|
||||
let view;
|
||||
const message = 'This message will always increment the message counter from zero',
|
||||
sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
|
||||
msgFactory = function () {
|
||||
return $msg({
|
||||
@ -1254,24 +1267,29 @@
|
||||
// leave converse-chat page
|
||||
_converse.windowState = 'hidden';
|
||||
_converse.chatboxes.onMessage(msgFactory());
|
||||
expect(_converse.msg_counter).toBe(1);
|
||||
return test_utils.waitUntil(() => _converse.api.chats.get().length)
|
||||
.then(() => {
|
||||
expect(_converse.msg_counter).toBe(1);
|
||||
|
||||
// come back to converse-chat page
|
||||
_converse.saveWindowState(null, 'focus');
|
||||
var view = _converse.chatboxviews.get(sender_jid);
|
||||
expect(u.isVisible(view.el)).toBeTruthy();
|
||||
expect(_converse.msg_counter).toBe(0);
|
||||
// come back to converse-chat page
|
||||
_converse.saveWindowState(null, 'focus');
|
||||
view = _converse.chatboxviews.get(sender_jid);
|
||||
expect(u.isVisible(view.el)).toBeTruthy();
|
||||
expect(_converse.msg_counter).toBe(0);
|
||||
|
||||
// close chatbox and leave converse-chat page again
|
||||
view.close();
|
||||
_converse.windowState = 'hidden';
|
||||
// close chatbox and leave converse-chat page again
|
||||
view.close();
|
||||
_converse.windowState = 'hidden';
|
||||
|
||||
// check that msg_counter is incremented from zero again
|
||||
_converse.chatboxes.onMessage(msgFactory());
|
||||
view = _converse.chatboxviews.get(sender_jid);
|
||||
expect(u.isVisible(view.el)).toBeTruthy();
|
||||
expect(_converse.msg_counter).toBe(1);
|
||||
done();
|
||||
// check that msg_counter is incremented from zero again
|
||||
_converse.chatboxes.onMessage(msgFactory());
|
||||
return test_utils.waitUntil(() => _converse.api.chats.get().length)
|
||||
}).then(() => {
|
||||
view = _converse.chatboxviews.get(sender_jid);
|
||||
expect(u.isVisible(view.el)).toBeTruthy();
|
||||
expect(_converse.msg_counter).toBe(1);
|
||||
done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
@ -1288,13 +1306,17 @@
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost',
|
||||
msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
|
||||
let view;
|
||||
test_utils.openChatBoxFor(_converse, sender_jid)
|
||||
.then((view) => {
|
||||
.then((v) => {
|
||||
view = v;
|
||||
view.model.save('scrolled', true);
|
||||
_converse.chatboxes.onMessage(msg);
|
||||
return test_utils.waitUntil(() => view.model.messages.length);
|
||||
}).then(() => {
|
||||
expect(view.model.get('num_unread')).toBe(1);
|
||||
done();
|
||||
});
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
|
||||
it("is not incremented when the message is received and ChatBoxView is scrolled down",
|
||||
@ -1323,15 +1345,18 @@
|
||||
test_utils.createContacts(_converse, 'current');
|
||||
_converse.emit('rosterContactsFetched');
|
||||
|
||||
var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
var msgFactory = function () {
|
||||
let chatbox;
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
const msgFactory = function () {
|
||||
return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
};
|
||||
test_utils.openChatBoxFor(_converse, sender_jid)
|
||||
.then(() => {
|
||||
var chatbox = _converse.chatboxes.get(sender_jid);
|
||||
chatbox = _converse.chatboxes.get(sender_jid);
|
||||
_converse.windowState = 'hidden';
|
||||
_converse.chatboxes.onMessage(msgFactory());
|
||||
return test_utils.waitUntil(() => chatbox.messages.length);
|
||||
}).then(() => {
|
||||
expect(chatbox.get('num_unread')).toBe(1);
|
||||
done();
|
||||
});
|
||||
@ -1344,17 +1369,19 @@
|
||||
|
||||
test_utils.createContacts(_converse, 'current');
|
||||
_converse.emit('rosterContactsFetched');
|
||||
|
||||
var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
var msgFactory = function () {
|
||||
let chatbox;
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
const msgFactory = function () {
|
||||
return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
};
|
||||
test_utils.openChatBoxFor(_converse, sender_jid)
|
||||
.then(() => {
|
||||
var chatbox = _converse.chatboxes.get(sender_jid);
|
||||
chatbox = _converse.chatboxes.get(sender_jid);
|
||||
chatbox.save('scrolled', true);
|
||||
_converse.windowState = 'hidden';
|
||||
_converse.chatboxes.onMessage(msgFactory());
|
||||
return test_utils.waitUntil(() => chatbox.messages.length);
|
||||
}).then(() => {
|
||||
expect(chatbox.get('num_unread')).toBe(1);
|
||||
done();
|
||||
});
|
||||
@ -1368,20 +1395,23 @@
|
||||
test_utils.createContacts(_converse, 'current');
|
||||
_converse.emit('rosterContactsFetched');
|
||||
|
||||
let chatbox;
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
var msgFactory = function () {
|
||||
const msgFactory = function () {
|
||||
return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
};
|
||||
test_utils.openChatBoxFor(_converse, sender_jid)
|
||||
.then(() => {
|
||||
const chatbox = _converse.chatboxes.get(sender_jid);
|
||||
chatbox = _converse.chatboxes.get(sender_jid);
|
||||
_converse.windowState = 'hidden';
|
||||
_converse.chatboxes.onMessage(msgFactory());
|
||||
return test_utils.waitUntil(() => chatbox.messages.length);
|
||||
}).then(() => {
|
||||
expect(chatbox.get('num_unread')).toBe(1);
|
||||
_converse.saveWindowState(null, 'focus');
|
||||
expect(chatbox.get('num_unread')).toBe(0);
|
||||
done();
|
||||
});
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
|
||||
it("is not cleared when ChatBoxView was scrolled up and the windows become focused",
|
||||
@ -1391,21 +1421,24 @@
|
||||
|
||||
test_utils.createContacts(_converse, 'current');
|
||||
_converse.emit('rosterContactsFetched');
|
||||
var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
var msgFactory = function () {
|
||||
let chatbox;
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
const msgFactory = function () {
|
||||
return test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
};
|
||||
test_utils.openChatBoxFor(_converse, sender_jid)
|
||||
.then(() => {
|
||||
var chatbox = _converse.chatboxes.get(sender_jid);
|
||||
chatbox = _converse.chatboxes.get(sender_jid);
|
||||
chatbox.save('scrolled', true);
|
||||
_converse.windowState = 'hidden';
|
||||
_converse.chatboxes.onMessage(msgFactory());
|
||||
return test_utils.waitUntil(() => chatbox.messages.length);
|
||||
}).then(() => {
|
||||
expect(chatbox.get('num_unread')).toBe(1);
|
||||
_converse.saveWindowState(null, 'focus');
|
||||
expect(chatbox.get('num_unread')).toBe(1);
|
||||
done();
|
||||
});
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
});
|
||||
|
||||
@ -1419,28 +1452,29 @@
|
||||
test_utils.createContacts(_converse, 'current');
|
||||
_converse.emit('rosterContactsFetched');
|
||||
|
||||
let msg, chatbox, indicator_el, selector;
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length, 500)
|
||||
.then(() => test_utils.openChatBoxFor(_converse, sender_jid))
|
||||
.then(() => {
|
||||
var chatbox = _converse.chatboxes.get(sender_jid);
|
||||
chatbox = _converse.chatboxes.get(sender_jid);
|
||||
chatbox.save('scrolled', true);
|
||||
|
||||
var msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
_converse.chatboxes.onMessage(msg);
|
||||
|
||||
var selector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
|
||||
indicator_el = sizzle(selector, _converse.rosterview.el).pop();
|
||||
|
||||
return test_utils.waitUntil(() => chatbox.messages.length);
|
||||
}).then(() => {
|
||||
selector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator';
|
||||
indicator_el = sizzle(selector, _converse.rosterview.el).pop();
|
||||
expect(indicator_el.textContent).toBe('1');
|
||||
|
||||
msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread too');
|
||||
_converse.chatboxes.onMessage(msg);
|
||||
|
||||
return test_utils.waitUntil(() => chatbox.messages.length);
|
||||
}).then(() => {
|
||||
indicator_el = sizzle(selector, _converse.rosterview.el).pop();
|
||||
expect(indicator_el.textContent).toBe('2');
|
||||
done();
|
||||
});
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
|
||||
it("is updated when message is received and chatbox is minimized",
|
||||
@ -1452,28 +1486,30 @@
|
||||
_converse.emit('rosterContactsFetched');
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
|
||||
let chatbox, indicator_el, msg, selector;
|
||||
test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length, 500)
|
||||
.then(() => test_utils.openChatBoxFor(_converse, sender_jid))
|
||||
.then(() => {
|
||||
var chatbox = _converse.chatboxes.get(sender_jid);
|
||||
chatbox = _converse.chatboxes.get(sender_jid);
|
||||
var chatboxview = _converse.chatboxviews.get(sender_jid);
|
||||
chatboxview.minimize();
|
||||
|
||||
var msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread');
|
||||
_converse.chatboxes.onMessage(msg);
|
||||
|
||||
var selector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
|
||||
indicator_el = sizzle(selector, _converse.rosterview.el).pop();
|
||||
|
||||
return test_utils.waitUntil(() => chatbox.messages.length);
|
||||
}).then(() => {
|
||||
selector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator';
|
||||
indicator_el = sizzle(selector, _converse.rosterview.el).pop();
|
||||
expect(indicator_el.textContent).toBe('1');
|
||||
|
||||
msg = test_utils.createChatMessage(_converse, sender_jid, 'This message will be unread too');
|
||||
_converse.chatboxes.onMessage(msg);
|
||||
|
||||
return test_utils.waitUntil(() => chatbox.messages.length);
|
||||
}).then(() => {
|
||||
indicator_el = sizzle(selector, _converse.rosterview.el).pop();
|
||||
expect(indicator_el.textContent).toBe('2');
|
||||
done();
|
||||
});
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
|
||||
it("is cleared when chatbox is maximzied after receiving messages in minimized mode",
|
||||
@ -1484,27 +1520,28 @@
|
||||
test_utils.createContacts(_converse, 'current');
|
||||
_converse.emit('rosterContactsFetched');
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
|
||||
let chatbox, view, select_msgs_indicator;
|
||||
const msgFactory = function () {
|
||||
return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
|
||||
};
|
||||
test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length, 500)
|
||||
.then(() => test_utils.openChatBoxFor(_converse, sender_jid))
|
||||
.then(() => {
|
||||
var chatbox = _converse.chatboxes.get(sender_jid);
|
||||
var chatboxview = _converse.chatboxviews.get(sender_jid);
|
||||
chatbox = _converse.chatboxes.get(sender_jid);
|
||||
view = _converse.chatboxviews.get(sender_jid);
|
||||
var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator';
|
||||
var selectMsgsIndicator = () => $(_converse.rosterview.el).find(msgsIndicatorSelector);
|
||||
var msgFactory = function () {
|
||||
return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
|
||||
};
|
||||
chatboxview.minimize();
|
||||
|
||||
select_msgs_indicator = () => $(_converse.rosterview.el).find(msgsIndicatorSelector);
|
||||
view.minimize();
|
||||
_converse.chatboxes.onMessage(msgFactory());
|
||||
expect(selectMsgsIndicator().text()).toBe('1');
|
||||
|
||||
return test_utils.waitUntil(() => chatbox.messages.length);
|
||||
}).then(() => {
|
||||
expect(select_msgs_indicator().text()).toBe('1');
|
||||
_converse.chatboxes.onMessage(msgFactory());
|
||||
expect(selectMsgsIndicator().text()).toBe('2');
|
||||
|
||||
chatboxview.maximize();
|
||||
expect(selectMsgsIndicator().length).toBe(0);
|
||||
return test_utils.waitUntil(() => chatbox.messages.length);
|
||||
}).then(() => {
|
||||
expect(select_msgs_indicator().text()).toBe('2');
|
||||
view.maximize();
|
||||
expect(select_msgs_indicator().length).toBe(0);
|
||||
done();
|
||||
});
|
||||
}));
|
||||
@ -1518,27 +1555,27 @@
|
||||
_converse.emit('rosterContactsFetched');
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
|
||||
let view, chatbox, select_msgs_indicator;
|
||||
test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length, 500)
|
||||
.then(() => test_utils.openChatBoxFor(_converse, sender_jid))
|
||||
.then(() => {
|
||||
var chatbox = _converse.chatboxes.get(sender_jid);
|
||||
var chatboxview = _converse.chatboxviews.get(sender_jid);
|
||||
chatbox = _converse.chatboxes.get(sender_jid);
|
||||
view = _converse.chatboxviews.get(sender_jid);
|
||||
var msgFactory = function () {
|
||||
return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
|
||||
};
|
||||
var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
|
||||
selectMsgsIndicator = () => $(_converse.rosterview.el).find(msgsIndicatorSelector);
|
||||
|
||||
var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator';
|
||||
select_msgs_indicator = () => $(_converse.rosterview.el).find(msgsIndicatorSelector);
|
||||
chatbox.save('scrolled', true);
|
||||
|
||||
_converse.chatboxes.onMessage(msgFactory());
|
||||
expect(selectMsgsIndicator().text()).toBe('1');
|
||||
|
||||
chatboxview.viewUnreadMessages();
|
||||
return test_utils.waitUntil(() => view.model.messages.length);
|
||||
}).then(() => {
|
||||
expect(select_msgs_indicator().text()).toBe('1');
|
||||
view.viewUnreadMessages();
|
||||
_converse.rosterview.render();
|
||||
expect(selectMsgsIndicator().length).toBe(0);
|
||||
expect(select_msgs_indicator().length).toBe(0);
|
||||
done();
|
||||
});
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
|
||||
it("is not cleared after user clicks on roster view when chatbox is already opened and scrolled up",
|
||||
@ -1550,24 +1587,25 @@
|
||||
_converse.emit('rosterContactsFetched');
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
|
||||
let select_msgs_indicator, view;
|
||||
test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll('.roster-group').length, 500)
|
||||
.then(() => test_utils.openChatBoxFor(_converse, sender_jid))
|
||||
.then(() => {
|
||||
var chatbox = _converse.chatboxes.get(sender_jid);
|
||||
var chatboxview = _converse.chatboxviews.get(sender_jid);
|
||||
const chatbox = _converse.chatboxes.get(sender_jid);
|
||||
view = _converse.chatboxviews.get(sender_jid);
|
||||
var msgFactory = function () {
|
||||
return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
|
||||
};
|
||||
var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator',
|
||||
selectMsgsIndicator = () => $(_converse.rosterview.el).find(msgsIndicatorSelector);
|
||||
|
||||
var msgsIndicatorSelector = 'a.open-chat:contains("' + chatbox.get('fullname') + '") .msgs-indicator';
|
||||
select_msgs_indicator = () => $(_converse.rosterview.el).find(msgsIndicatorSelector);
|
||||
chatbox.save('scrolled', true);
|
||||
|
||||
_converse.chatboxes.onMessage(msgFactory());
|
||||
expect(selectMsgsIndicator().text()).toBe('1');
|
||||
|
||||
test_utils.openChatBoxFor(_converse, sender_jid);
|
||||
expect(selectMsgsIndicator().text()).toBe('1');
|
||||
return test_utils.waitUntil(() => view.model.messages.length);
|
||||
}).then(() => {
|
||||
expect(select_msgs_indicator().text()).toBe('1');
|
||||
return test_utils.openChatBoxFor(_converse, sender_jid);
|
||||
}).then(() => {
|
||||
expect(select_msgs_indicator().text()).toBe('1');
|
||||
done();
|
||||
});
|
||||
}));
|
||||
@ -1584,20 +1622,21 @@
|
||||
_converse.emit('rosterContactsFetched');
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
|
||||
let selectUnreadMsgCount;
|
||||
test_utils.openChatBoxFor(_converse, sender_jid)
|
||||
.then(() => {
|
||||
const msgFactory = function () {
|
||||
return test_utils.createChatMessage(_converse, sender_jid, 'This message will be received as unread, but eventually will be read');
|
||||
};
|
||||
const selectUnreadMsgCount = function () {
|
||||
selectUnreadMsgCount = function () {
|
||||
const minimizedChatBoxView = _converse.minimized_chats.get(sender_jid);
|
||||
return minimizedChatBoxView.el.querySelector('.message-count');
|
||||
};
|
||||
|
||||
const chatbox = _converse.chatboxes.get(sender_jid);
|
||||
chatbox.save('scrolled', true);
|
||||
_converse.chatboxes.onMessage(msgFactory());
|
||||
|
||||
return test_utils.waitUntil(() => chatbox.messages.length);
|
||||
}).then(() => {
|
||||
const chatboxview = _converse.chatboxviews.get(sender_jid);
|
||||
chatboxview.minimize();
|
||||
|
||||
@ -1605,7 +1644,7 @@
|
||||
expect(u.isVisible(unread_count)).toBeTruthy();
|
||||
expect(unread_count.innerHTML).toBe('1');
|
||||
done();
|
||||
});
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
|
||||
it("is incremented when message is received and windows is not focused",
|
||||
@ -1617,27 +1656,27 @@
|
||||
_converse.emit('rosterContactsFetched');
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
|
||||
let selectUnreadMsgCount;
|
||||
test_utils.openChatBoxFor(_converse, sender_jid)
|
||||
.then(() => {
|
||||
const msgFactory = function () {
|
||||
return test_utils.createChatMessage(_converse, sender_jid,
|
||||
'This message will be received as unread, but eventually will be read');
|
||||
};
|
||||
const selectUnreadMsgCount = function () {
|
||||
selectUnreadMsgCount = function () {
|
||||
const minimizedChatBoxView = _converse.minimized_chats.get(sender_jid);
|
||||
return minimizedChatBoxView.el.querySelector('.message-count');
|
||||
};
|
||||
|
||||
const chatboxview = _converse.chatboxviews.get(sender_jid);
|
||||
chatboxview.minimize();
|
||||
|
||||
const view = _converse.chatboxviews.get(sender_jid);
|
||||
view.minimize();
|
||||
_converse.chatboxes.onMessage(msgFactory());
|
||||
|
||||
return test_utils.waitUntil(() => view.model.messages.length);
|
||||
}).then(() => {
|
||||
const unread_count = selectUnreadMsgCount();
|
||||
expect(u.isVisible(unread_count)).toBeTruthy();
|
||||
expect(unread_count.innerHTML).toBe('1');
|
||||
done();
|
||||
});
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
|
||||
it("will render Openstreetmap-URL from geo-URI",
|
||||
|
@ -738,7 +738,6 @@
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
|
||||
jasmine.clock().tick(ONE_DAY_LATER);
|
||||
|
||||
// Test a user leaving a groupchat
|
||||
presence = $pres({
|
||||
to: 'dummy@localhost/_converse.js-29092160',
|
||||
@ -766,11 +765,9 @@
|
||||
expect($chat_content.find('div.chat-info:last').html()).toBe(
|
||||
'newguy has left the groupchat. '+
|
||||
'"Disconnected: Replaced by new connection"');
|
||||
|
||||
jasmine.clock().uninstall();
|
||||
done();
|
||||
return;
|
||||
});
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
|
||||
it("shows its description in the chat heading",
|
||||
@ -827,14 +824,14 @@
|
||||
null, ['rosterGroupsFetched'], {},
|
||||
function (done, _converse) {
|
||||
|
||||
let view;
|
||||
test_utils.waitUntilDiscoConfirmed(_converse, 'localhost', [], ['vcard-temp'])
|
||||
.then(function () {
|
||||
return test_utils.waitUntil(() => _converse.xmppstatus.vcard.get('fullname'))
|
||||
}).then(function () {
|
||||
.then(() => test_utils.waitUntil(() => _converse.xmppstatus.vcard.get('fullname')))
|
||||
.then(() => {
|
||||
test_utils.createContacts(_converse, 'current');
|
||||
return test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy');
|
||||
}).then(function () {
|
||||
var view = _converse.chatboxviews.get('lounge@localhost');
|
||||
}).then(() => {
|
||||
view = _converse.chatboxviews.get('lounge@localhost');
|
||||
if (!$(view.el).find('.chat-area').length) { view.renderChatArea(); }
|
||||
var message = '/me is tired';
|
||||
var nick = mock.chatroom_names[0],
|
||||
@ -1495,20 +1492,18 @@
|
||||
null, ['rosterGroupsFetched'], {},
|
||||
function (done, _converse) {
|
||||
|
||||
let view;
|
||||
const text = 'This is a received message';
|
||||
test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy').then(function () {
|
||||
spyOn(_converse, 'emit');
|
||||
var view = _converse.chatboxviews.get('lounge@localhost');
|
||||
|
||||
|
||||
view = _converse.chatboxviews.get('lounge@localhost');
|
||||
if (!$(view.el).find('.chat-area').length) { view.renderChatArea(); }
|
||||
var nick = mock.chatroom_names[0];
|
||||
|
||||
view.model.occupants.create({
|
||||
'nick': nick,
|
||||
'muc_jid': `${view.model.get('jid')}/${nick}`
|
||||
});
|
||||
|
||||
var text = 'This is a received message';
|
||||
var message = $msg({
|
||||
from: 'lounge@localhost/'+nick,
|
||||
id: '1',
|
||||
@ -1585,7 +1580,7 @@
|
||||
}).c('body').t('Message: '+i).tree());
|
||||
}
|
||||
// Give enough time for `markScrolled` to have been called
|
||||
setTimeout(function () {
|
||||
setTimeout(() => {
|
||||
view.content.scrollTop = 0;
|
||||
view.model.onMessage(
|
||||
$msg({
|
||||
@ -3435,19 +3430,19 @@
|
||||
var roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
|
||||
expect(roomspanel.el.querySelectorAll('.available-room').length).toBe(0);
|
||||
|
||||
var room_jid = 'kitchen@conference.shakespeare.lit';
|
||||
test_utils.openAndEnterChatRoom(
|
||||
_converse, 'kitchen', 'conference.shakespeare.lit', 'fires').then(function () {
|
||||
|
||||
let view, nick;
|
||||
const room_jid = 'kitchen@conference.shakespeare.lit';
|
||||
const message = 'fires: Your attention is required';
|
||||
test_utils.openAndEnterChatRoom(_converse, 'kitchen', 'conference.shakespeare.lit', 'fires')
|
||||
.then(() => {
|
||||
expect(roomspanel.el.querySelectorAll('.available-room').length).toBe(1);
|
||||
expect(roomspanel.el.querySelectorAll('.msgs-indicator').length).toBe(0);
|
||||
|
||||
var view = _converse.chatboxviews.get(room_jid);
|
||||
view = _converse.chatboxviews.get(room_jid);
|
||||
view.model.set({'minimized': true});
|
||||
|
||||
var contact_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
var message = 'fires: Your attention is required';
|
||||
var nick = mock.chatroom_names[0];
|
||||
nick = mock.chatroom_names[0];
|
||||
|
||||
view.model.onMessage($msg({
|
||||
from: room_jid+'/'+nick,
|
||||
@ -3455,7 +3450,8 @@
|
||||
to: 'dummy@localhost',
|
||||
type: 'groupchat'
|
||||
}).c('body').t(message).tree());
|
||||
|
||||
return test_utils.waitUntil(() => view.model.messages.length);
|
||||
}).then(() => {
|
||||
expect(roomspanel.el.querySelectorAll('.available-room').length).toBe(1);
|
||||
expect(roomspanel.el.querySelectorAll('.msgs-indicator').length).toBe(1);
|
||||
expect(roomspanel.el.querySelector('.msgs-indicator').textContent).toBe('1');
|
||||
@ -3466,17 +3462,16 @@
|
||||
'to': 'dummy@localhost',
|
||||
'type': 'groupchat'
|
||||
}).c('body').t(message).tree());
|
||||
|
||||
return test_utils.waitUntil(() => view.model.messages.length > 1);
|
||||
}).then(() => {
|
||||
expect(roomspanel.el.querySelectorAll('.available-room').length).toBe(1);
|
||||
expect(roomspanel.el.querySelectorAll('.msgs-indicator').length).toBe(1);
|
||||
expect(roomspanel.el.querySelector('.msgs-indicator').textContent).toBe('2');
|
||||
|
||||
view.model.set({'minimized': false});
|
||||
|
||||
expect(roomspanel.el.querySelectorAll('.available-room').length).toBe(1);
|
||||
expect(roomspanel.el.querySelectorAll('.msgs-indicator').length).toBe(0);
|
||||
done();
|
||||
});
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
|
||||
describe("A Chat Status Notification", function () {
|
||||
@ -3488,14 +3483,15 @@
|
||||
null, ['rosterGroupsFetched'], {},
|
||||
function (done, _converse) {
|
||||
|
||||
let view;
|
||||
const room_jid = 'coven@chat.shakespeare.lit';
|
||||
test_utils.openAndEnterChatRoom(
|
||||
_converse, 'coven', 'chat.shakespeare.lit', 'some1').then(function () {
|
||||
|
||||
var room_jid = 'coven@chat.shakespeare.lit';
|
||||
var view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
|
||||
var $chat_content = $(view.el).find('.chat-content');
|
||||
view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
|
||||
const chat_content = view.el.querySelector('.chat-content');
|
||||
|
||||
expect($chat_content.find('div.chat-info:first').html()).toBe("some1 has entered the groupchat");
|
||||
expect($(chat_content).find('div.chat-info:first').html()).toBe("some1 has entered the groupchat");
|
||||
|
||||
let presence = $pres({
|
||||
to: 'dummy@localhost/_converse.js-29092160',
|
||||
@ -3508,8 +3504,8 @@
|
||||
'role': 'participant'
|
||||
});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(presence));
|
||||
expect($chat_content[0].querySelectorAll('div.chat-info').length).toBe(2);
|
||||
expect($chat_content.find('div.chat-info:last').html()).toBe("newguy has entered the groupchat");
|
||||
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(2);
|
||||
expect($(chat_content).find('div.chat-info:last').html()).toBe("newguy has entered the groupchat");
|
||||
|
||||
presence = $pres({
|
||||
to: 'dummy@localhost/_converse.js-29092160',
|
||||
@ -3522,8 +3518,8 @@
|
||||
'role': 'participant'
|
||||
});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(presence));
|
||||
expect($chat_content[0].querySelectorAll('div.chat-info').length).toBe(3);
|
||||
expect($chat_content.find('div.chat-info:last').html()).toBe("nomorenicks has entered the groupchat");
|
||||
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(3);
|
||||
expect($(chat_content).find('div.chat-info:last').html()).toBe("nomorenicks has entered the groupchat");
|
||||
|
||||
// See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
|
||||
|
||||
@ -3536,7 +3532,8 @@
|
||||
}).c('body').c('composing', {'xmlns': Strophe.NS.CHATSTATES}).tree();
|
||||
|
||||
view.model.onMessage(msg);
|
||||
|
||||
return test_utils.waitUntil(() => view.el.querySelectorAll('.chat-state-notification').length);
|
||||
}).then(() => {
|
||||
// Check that the notification appears inside the chatbox in the DOM
|
||||
var events = view.el.querySelectorAll('.chat-event');
|
||||
expect(events.length).toBe(3);
|
||||
@ -3554,7 +3551,7 @@
|
||||
});
|
||||
|
||||
// Check that it doesn't appear twice
|
||||
msg = $msg({
|
||||
let msg = $msg({
|
||||
from: room_jid+'/newguy',
|
||||
id: (new Date()).getTime(),
|
||||
to: 'dummy@localhost',
|
||||
@ -3632,7 +3629,7 @@
|
||||
notifications = view.el.querySelectorAll('.chat-state-notification');
|
||||
expect(notifications.length).toBe(0);
|
||||
done();
|
||||
});
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
});
|
||||
|
||||
@ -3776,7 +3773,7 @@
|
||||
expect(notifications[0].textContent).toEqual('nomorenicks is typing');
|
||||
expect(notifications[1].textContent).toEqual('newguy has stopped typing');
|
||||
done();
|
||||
});
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
@ -1,11 +1,12 @@
|
||||
(function (root, factory) {
|
||||
define(["jquery", "jasmine", "mock", "test-utils"], factory);
|
||||
} (this, function ($, jasmine, mock, test_utils) {
|
||||
var _ = converse.env._;
|
||||
var $pres = converse.env.$pres;
|
||||
var $msg = converse.env.$msg;
|
||||
var $iq = converse.env.$iq;
|
||||
var u = converse.env.utils;
|
||||
const _ = converse.env._,
|
||||
$pres = converse.env.$pres,
|
||||
$msg = converse.env.$msg,
|
||||
$iq = converse.env.$iq,
|
||||
u = converse.env.utils,
|
||||
Strophe = converse.env.Strophe;
|
||||
|
||||
|
||||
describe("The Controlbox", function () {
|
||||
@ -72,18 +73,18 @@
|
||||
test_utils.createContacts(_converse, 'all').openControlBox();
|
||||
_converse.emit('rosterContactsFetched');
|
||||
|
||||
let chatview;
|
||||
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
|
||||
test_utils.openChatBoxFor(_converse, sender_jid);
|
||||
return test_utils.waitUntil(() => _converse.chatboxes.length).then(() => {
|
||||
|
||||
const chatview = _converse.chatboxviews.get(sender_jid);
|
||||
chatview = _converse.chatboxviews.get(sender_jid);
|
||||
chatview.model.set({'minimized': true});
|
||||
|
||||
expect(_.isNull(_converse.chatboxviews.el.querySelector('.restore-chat .message-count'))).toBeTruthy();
|
||||
expect(_.isNull(_converse.rosterview.el.querySelector('.msgs-indicator'))).toBeTruthy();
|
||||
|
||||
var msg = $msg({
|
||||
const msg = $msg({
|
||||
from: sender_jid,
|
||||
to: _converse.connection.jid,
|
||||
type: 'chat',
|
||||
@ -91,10 +92,13 @@
|
||||
}).c('body').t('hello').up()
|
||||
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
|
||||
_converse.chatboxes.onMessage(msg);
|
||||
return test_utils.waitUntil(() => _converse.rosterview.el.querySelectorAll(".msgs-indicator"));
|
||||
}).then(() => {
|
||||
spyOn(chatview.model, 'incrementUnreadMsgCounter').and.callThrough();
|
||||
expect(_converse.chatboxviews.el.querySelector('.restore-chat .message-count').textContent).toBe('1');
|
||||
expect(_converse.rosterview.el.querySelector('.msgs-indicator').textContent).toBe('1');
|
||||
|
||||
msg = $msg({
|
||||
const msg = $msg({
|
||||
from: sender_jid,
|
||||
to: _converse.connection.jid,
|
||||
type: 'chat',
|
||||
@ -102,14 +106,15 @@
|
||||
}).c('body').t('hello again').up()
|
||||
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
|
||||
_converse.chatboxes.onMessage(msg);
|
||||
return test_utils.waitUntil(() => chatview.model.incrementUnreadMsgCounter.calls.count());
|
||||
}).then(() => {
|
||||
expect(_converse.chatboxviews.el.querySelector('.restore-chat .message-count').textContent).toBe('2');
|
||||
expect(_converse.rosterview.el.querySelector('.msgs-indicator').textContent).toBe('2');
|
||||
|
||||
chatview.model.set({'minimized': false});
|
||||
expect(_.isNull(_converse.chatboxviews.el.querySelector('.restore-chat .message-count'))).toBeTruthy();
|
||||
expect(_.isNull(_converse.rosterview.el.querySelector('.msgs-indicator'))).toBeTruthy();
|
||||
done();
|
||||
});
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
|
||||
}));
|
||||
});
|
||||
|
||||
|
@ -311,7 +311,7 @@
|
||||
}).catch(_.partial(console.error, _));
|
||||
}));
|
||||
|
||||
it("has a method 'open' which opens and returns promise that resolves to a chat model", mock.initConverseWithPromises(
|
||||
it("has a method 'open' which opens and returns a promise that resolves to a chat model", mock.initConverseWithPromises(
|
||||
null, ['rosterGroupsFetched', 'chatBoxesInitialized'], {}, function (done, _converse) {
|
||||
|
||||
test_utils.openControlBox();
|
||||
|
30
spec/mam.js
30
spec/mam.js
@ -20,15 +20,27 @@
|
||||
null, ['discoInitialized'], {},
|
||||
function (done, _converse) {
|
||||
|
||||
test_utils.openAndEnterChatRoom(_converse, 'trek-radio', 'conference.lightwitch.org', 'jcbrand').then(function () {
|
||||
var chatroomview = _converse.chatboxviews.get('trek-radio@conference.lightwitch.org');
|
||||
var stanza = Strophe.xmlHtmlNode(
|
||||
let view, stanza;
|
||||
|
||||
test_utils.openAndEnterChatRoom(_converse, 'trek-radio', 'conference.lightwitch.org', 'jcbrand')
|
||||
.then(() => {
|
||||
view = _converse.chatboxviews.get('trek-radio@conference.lightwitch.org');
|
||||
stanza = Strophe.xmlHtmlNode(
|
||||
`<message xmlns="jabber:client" to="jcbrand@lightwitch.org/converse.js-73057452" type="groupchat" from="trek-radio@conference.lightwitch.org/comndrdukath#0805 (STO)">
|
||||
<body>negan</body>
|
||||
<stanza-id xmlns="urn:xmpp:sid:0" id="45fbbf2a-1059-479d-9283-c8effaf05621" by="trek-radio@conference.lightwitch.org"/>
|
||||
</message>`).firstElementChild;
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
|
||||
return test_utils.waitUntil(() => view.content.querySelectorAll('.chat-msg').length)
|
||||
}).then(() => {
|
||||
// XXX: we wait here until the first message appears before
|
||||
// sending the duplicate. If we don't do that, then the
|
||||
// duplicate appears before the promise for `createMessage`
|
||||
// has been resolved, which means that the `isDuplicate`
|
||||
// check fails because the first message doesn't exist yet.
|
||||
//
|
||||
// Not sure whether such a race-condition might pose a problem
|
||||
// in "real-world" situations.
|
||||
stanza = Strophe.xmlHtmlNode(
|
||||
`<message xmlns="jabber:client" to="jcbrand@lightwitch.org/converse.js-73057452">
|
||||
<result xmlns="urn:xmpp:mam:2" queryid="82d9db27-6cf8-4787-8c2c-5a560263d823" id="45fbbf2a-1059-479d-9283-c8effaf05621">
|
||||
@ -39,10 +51,14 @@
|
||||
</forwarded>
|
||||
</result>
|
||||
</message>`).firstElementChild;
|
||||
chatroomview.model.onMessage(stanza);
|
||||
expect(chatroomview.content.querySelectorAll('.chat-msg').length).toBe(1);
|
||||
|
||||
spyOn(view.model, 'isDuplicate').and.callThrough();
|
||||
view.model.onMessage(stanza);
|
||||
return test_utils.waitUntil(() => view.model.isDuplicate.calls.count());
|
||||
}).then(() => {
|
||||
expect(view.content.querySelectorAll('.chat-msg').length).toBe(1);
|
||||
done();
|
||||
});
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
|
||||
}))
|
||||
});
|
||||
|
||||
|
639
spec/messages.js
639
spec/messages.js
File diff suppressed because it is too large
Load Diff
@ -4,6 +4,7 @@
|
||||
const _ = converse.env._;
|
||||
const $msg = converse.env.$msg;
|
||||
const u = converse.env.utils;
|
||||
const Strophe = converse.env.Strophe;
|
||||
|
||||
describe("The Minimized Chats Widget", function () {
|
||||
|
||||
@ -43,7 +44,7 @@
|
||||
expect(_converse.minimized_chats.keys().length).toBe(2);
|
||||
expect(_.includes(_converse.minimized_chats.keys(), contact_jid)).toBeTruthy();
|
||||
done();
|
||||
});
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
|
||||
it("can be toggled to hide or show minimized chats",
|
||||
@ -74,7 +75,7 @@
|
||||
}).then(() => {
|
||||
expect(_converse.minimized_chats.toggleview.model.get('collapsed')).toBeTruthy();
|
||||
done();
|
||||
});
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
|
||||
it("shows the number messages received to minimized chats",
|
||||
@ -99,7 +100,7 @@
|
||||
contact_jid = mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
test_utils.openChatBoxFor(_converse, contact_jid);
|
||||
}
|
||||
return test_utils.waitUntil(() => _converse.chatboxes.length == 4).then(() => {
|
||||
test_utils.waitUntil(() => _converse.chatboxes.length == 4).then(() => {
|
||||
for (i=0; i<3; i++) {
|
||||
chatview = _converse.chatboxviews.get(contact_jid);
|
||||
chatview.model.set({'minimized': true});
|
||||
@ -111,9 +112,11 @@
|
||||
}).c('body').t('This message is sent to a minimized chatbox').up()
|
||||
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
|
||||
_converse.chatboxes.onMessage(msg);
|
||||
expect($(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count')).is(':visible')).toBeTruthy();
|
||||
expect($(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count')).text()).toBe((i+1).toString());
|
||||
}
|
||||
return test_utils.waitUntil(() => chatview.model.messages.length);
|
||||
}).then(() => {
|
||||
expect(u.isVisible(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count'))).toBeTruthy();
|
||||
expect(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count').textContent).toBe((3).toString());
|
||||
// Chat state notifications don't increment the unread messages counter
|
||||
// <composing> state
|
||||
_converse.chatboxes.onMessage($msg({
|
||||
@ -122,7 +125,7 @@
|
||||
type: 'chat',
|
||||
id: (new Date()).getTime()
|
||||
}).c('composing', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
|
||||
expect($(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count')).text()).toBe((i).toString());
|
||||
expect(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count').textContent).toBe((i).toString());
|
||||
|
||||
// <paused> state
|
||||
_converse.chatboxes.onMessage($msg({
|
||||
@ -131,7 +134,7 @@
|
||||
type: 'chat',
|
||||
id: (new Date()).getTime()
|
||||
}).c('paused', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
|
||||
expect($(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count')).text()).toBe((i).toString());
|
||||
expect(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count').textContent).toBe((i).toString());
|
||||
|
||||
// <gone> state
|
||||
_converse.chatboxes.onMessage($msg({
|
||||
@ -140,7 +143,7 @@
|
||||
type: 'chat',
|
||||
id: (new Date()).getTime()
|
||||
}).c('gone', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
|
||||
expect($(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count')).text()).toBe((i).toString());
|
||||
expect(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count').textContent).toBe((i).toString());
|
||||
|
||||
// <inactive> state
|
||||
_converse.chatboxes.onMessage($msg({
|
||||
@ -149,9 +152,9 @@
|
||||
type: 'chat',
|
||||
id: (new Date()).getTime()
|
||||
}).c('inactive', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree());
|
||||
expect($(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count')).text()).toBe((i).toString());
|
||||
expect(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count').textContent).toBe((i).toString());
|
||||
done();
|
||||
});
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
|
||||
it("shows the number messages received to minimized groupchats",
|
||||
@ -159,7 +162,7 @@
|
||||
null, ['rosterGroupsFetched'], {},
|
||||
function (done, _converse) {
|
||||
|
||||
var room_jid = 'kitchen@conference.shakespeare.lit';
|
||||
const room_jid = 'kitchen@conference.shakespeare.lit';
|
||||
test_utils.openAndEnterChatRoom(
|
||||
_converse, 'kitchen', 'conference.shakespeare.lit', 'fires').then(function () {
|
||||
var view = _converse.chatboxviews.get(room_jid);
|
||||
@ -175,11 +178,12 @@
|
||||
type: 'groupchat'
|
||||
}).c('body').t(message).tree();
|
||||
view.model.onMessage(msg);
|
||||
|
||||
expect($(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count')).is(':visible')).toBeTruthy();
|
||||
expect($(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count')).text()).toBe('1');
|
||||
return test_utils.waitUntil(() => view.model.messages.length);
|
||||
}).then(() => {
|
||||
expect(u.isVisible(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count'))).toBeTruthy();
|
||||
expect(_converse.minimized_chats.toggleview.el.querySelector('.unread-message-count').textContent).toBe('1');
|
||||
done();
|
||||
});
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
});
|
||||
}));
|
||||
|
971
spec/omemo.js
Normal file
971
spec/omemo.js
Normal file
@ -0,0 +1,971 @@
|
||||
(function (root, factory) {
|
||||
define(["jasmine", "mock", "test-utils"], factory);
|
||||
} (this, function (jasmine, mock, test_utils) {
|
||||
var Strophe = converse.env.Strophe;
|
||||
var b64_sha1 = converse.env.b64_sha1;
|
||||
var $iq = converse.env.$iq;
|
||||
var $msg = converse.env.$msg;
|
||||
var _ = converse.env._;
|
||||
var u = converse.env.utils;
|
||||
|
||||
|
||||
function deviceListFetched (_converse, jid) {
|
||||
return _.get(_.filter(
|
||||
_converse.connection.IQ_stanzas,
|
||||
iq => iq.nodeTree.querySelector(`iq[to="${jid}"] items[node="eu.siacs.conversations.axolotl.devicelist"]`)
|
||||
).pop(), 'nodeTree');
|
||||
}
|
||||
|
||||
function ownDeviceHasBeenPublished (_converse) {
|
||||
return _.get(_.filter(
|
||||
_converse.connection.IQ_stanzas,
|
||||
iq => iq.nodeTree.querySelector('iq[from="'+_converse.bare_jid+'"] publish[node="eu.siacs.conversations.axolotl.devicelist"]')
|
||||
).pop(), 'nodeTree');
|
||||
}
|
||||
|
||||
function bundleHasBeenPublished (_converse) {
|
||||
return _.get(_.filter(
|
||||
_converse.connection.IQ_stanzas,
|
||||
iq => iq.nodeTree.querySelector('publish[node="eu.siacs.conversations.axolotl.bundles:123456789"]')
|
||||
).pop(), 'nodeTree');
|
||||
}
|
||||
|
||||
function bundleFetched (_converse, jid, device_id) {
|
||||
return _.get(_.filter(
|
||||
_converse.connection.IQ_stanzas,
|
||||
(iq) => iq.nodeTree.querySelector(`iq[to="${jid}"] items[node="eu.siacs.conversations.axolotl.bundles:${device_id}"]`)
|
||||
).pop(), 'nodeTree');
|
||||
}
|
||||
|
||||
function initializedOMEMO (_converse) {
|
||||
return test_utils.waitUntil(() => deviceListFetched(_converse, _converse.bare_jid))
|
||||
.then(iq_stanza => {
|
||||
const stanza = $iq({
|
||||
'from': _converse.bare_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result',
|
||||
}).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
|
||||
.c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
|
||||
.c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
|
||||
.c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
|
||||
.c('device', {'id': '482886413b977930064a5888b92134fe'});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
return test_utils.waitUntil(() => ownDeviceHasBeenPublished(_converse))
|
||||
}).then(iq_stanza => {
|
||||
const stanza = $iq({
|
||||
'from': _converse.bare_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result'});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
return test_utils.waitUntil(() => bundleHasBeenPublished(_converse))
|
||||
}).then(iq_stanza => {
|
||||
const stanza = $iq({
|
||||
'from': _converse.bare_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result'});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
return _converse.api.waitUntil('OMEMOInitialized');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
describe("The OMEMO module", function() {
|
||||
|
||||
it("adds methods for encrypting and decrypting messages via AES GCM",
|
||||
mock.initConverseWithPromises(
|
||||
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
|
||||
function (done, _converse) {
|
||||
|
||||
const message = 'This message will be encrypted'
|
||||
let view;
|
||||
test_utils.createContacts(_converse, 'current', 1);
|
||||
_converse.emit('rosterContactsFetched');
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
test_utils.openChatBoxFor(_converse, contact_jid)
|
||||
.then((v) => {
|
||||
view = v;
|
||||
return view.model.encryptMessage(message);
|
||||
}).then((payload) => {
|
||||
return view.model.decryptMessage(payload);
|
||||
}).then((result) => {
|
||||
expect(result).toBe(message);
|
||||
done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it("enables encrypted messages to be sent and received",
|
||||
mock.initConverseWithPromises(
|
||||
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
|
||||
function (done, _converse) {
|
||||
|
||||
let view, sent_stanza;
|
||||
test_utils.createContacts(_converse, 'current', 1);
|
||||
_converse.emit('rosterContactsFetched');
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
|
||||
return test_utils.waitUntil(() => initializedOMEMO(_converse))
|
||||
.then(() => test_utils.openChatBoxFor(_converse, contact_jid))
|
||||
.then(() => test_utils.waitUntil(() => deviceListFetched(_converse, contact_jid)))
|
||||
.then(iq_stanza => {
|
||||
const stanza = $iq({
|
||||
'from': contact_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.connection.jid,
|
||||
'type': 'result',
|
||||
}).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
|
||||
.c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
|
||||
.c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
|
||||
.c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
|
||||
.c('device', {'id': '555'});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
return test_utils.waitUntil(() => _converse.omemo_store);
|
||||
}).then(() => {
|
||||
const devicelist = _converse.devicelists.get({'jid': contact_jid});
|
||||
expect(devicelist.devices.length).toBe(1);
|
||||
|
||||
view = _converse.chatboxviews.get(contact_jid);
|
||||
view.model.set('omemo_active', true);
|
||||
|
||||
const textarea = view.el.querySelector('.chat-textarea');
|
||||
textarea.value = 'This message will be encrypted';
|
||||
view.keyPressed({
|
||||
target: textarea,
|
||||
preventDefault: _.noop,
|
||||
keyCode: 13 // Enter
|
||||
});
|
||||
return test_utils.waitUntil(() => bundleFetched(_converse, contact_jid, '555'));
|
||||
}).then((iq_stanza) => {
|
||||
const stanza = $iq({
|
||||
'from': contact_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result',
|
||||
}).c('pubsub', {
|
||||
'xmlns': 'http://jabber.org/protocol/pubsub'
|
||||
}).c('items', {'node': "eu.siacs.conversations.axolotl.bundles:555"})
|
||||
.c('item')
|
||||
.c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
|
||||
.c('signedPreKeyPublic', {'signedPreKeyId': '4223'}).t(btoa('1111')).up()
|
||||
.c('signedPreKeySignature').t(btoa('2222')).up()
|
||||
.c('identityKey').t(btoa('3333')).up()
|
||||
.c('prekeys')
|
||||
.c('preKeyPublic', {'preKeyId': '1'}).t(btoa('1001')).up()
|
||||
.c('preKeyPublic', {'preKeyId': '2'}).t(btoa('1002')).up()
|
||||
.c('preKeyPublic', {'preKeyId': '3'}).t(btoa('1003'));
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
|
||||
return test_utils.waitUntil(() => bundleFetched(_converse, _converse.bare_jid, '482886413b977930064a5888b92134fe'));
|
||||
}).then(iq_stanza => {
|
||||
const stanza = $iq({
|
||||
'from': _converse.bare_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result',
|
||||
}).c('pubsub', {
|
||||
'xmlns': 'http://jabber.org/protocol/pubsub'
|
||||
}).c('items', {'node': "eu.siacs.conversations.axolotl.bundles:482886413b977930064a5888b92134fe"})
|
||||
.c('item')
|
||||
.c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
|
||||
.c('signedPreKeyPublic', {'signedPreKeyId': '4223'}).t(btoa('100000')).up()
|
||||
.c('signedPreKeySignature').t(btoa('200000')).up()
|
||||
.c('identityKey').t(btoa('300000')).up()
|
||||
.c('prekeys')
|
||||
.c('preKeyPublic', {'preKeyId': '1'}).t(btoa('1991')).up()
|
||||
.c('preKeyPublic', {'preKeyId': '2'}).t(btoa('1992')).up()
|
||||
.c('preKeyPublic', {'preKeyId': '3'}).t(btoa('1993'));
|
||||
|
||||
spyOn(_converse.connection, 'send').and.callFake(stanza => { sent_stanza = stanza });
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
return test_utils.waitUntil(() => sent_stanza);
|
||||
}).then(() => {
|
||||
expect(sent_stanza.toLocaleString()).toBe(
|
||||
`<message from='dummy@localhost/resource' to='max.frankfurter@localhost' `+
|
||||
`type='chat' id='${sent_stanza.nodeTree.getAttribute('id')}' xmlns='jabber:client'>`+
|
||||
`<body>This is an OMEMO encrypted message which your client doesn’t seem to support. Find more information on https://conversations.im/omemo</body>`+
|
||||
`<encrypted xmlns='eu.siacs.conversations.axolotl'>`+
|
||||
`<header sid='123456789'>`+
|
||||
`<key rid='482886413b977930064a5888b92134fe'>eyJ0eXBlIjoxLCJib2R5IjoiYzFwaDNSNzNYNyIsInJlZ2lzdHJhdGlvbklkIjoiMTMzNyJ9</key>`+
|
||||
`<key rid='555'>eyJ0eXBlIjoxLCJib2R5IjoiYzFwaDNSNzNYNyIsInJlZ2lzdHJhdGlvbklkIjoiMTMzNyJ9</key>`+
|
||||
`<iv>${sent_stanza.nodeTree.querySelector('iv').textContent}</iv>`+
|
||||
`</header>`+
|
||||
`<payload>${sent_stanza.nodeTree.querySelector('payload').textContent}</payload>`+
|
||||
`</encrypted>`+
|
||||
`<store xmlns='urn:xmpp:hints'/>`+
|
||||
`</message>`);
|
||||
|
||||
// Test reception of an encrypted message
|
||||
return view.model.encryptMessage('This is an encrypted message from the contact')
|
||||
}).then((obj) => {
|
||||
// XXX: Normally the key will be encrypted via libsignal.
|
||||
// However, we're mocking libsignal in the tests, so we include
|
||||
// it as plaintext in the message.
|
||||
const key = btoa(JSON.stringify({
|
||||
'type': 1,
|
||||
'body': obj.key_and_tag,
|
||||
'registrationId': '1337'
|
||||
}));
|
||||
const stanza = $msg({
|
||||
'from': contact_jid,
|
||||
'to': _converse.connection.jid,
|
||||
'type': 'chat',
|
||||
'id': 'qwerty'
|
||||
}).c('body').t('This is a fallback message').up()
|
||||
.c('encrypted', {'xmlns': Strophe.NS.OMEMO})
|
||||
.c('header', {'sid': '555'})
|
||||
.c('key', {'rid': _converse.omemo_store.get('device_id')}).t(key).up()
|
||||
.c('iv').t(obj.iv)
|
||||
.up().up()
|
||||
.c('payload').t(obj.payload);
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
return test_utils.waitUntil(() => view.model.messages.length > 1);
|
||||
}).then(() => {
|
||||
expect(view.model.messages.length).toBe(2);
|
||||
const last_msg = view.model.messages.at(1);
|
||||
expect(view.el.querySelectorAll('.chat-msg__body')[1].textContent.trim())
|
||||
.toBe('This is an encrypted message from the contact');
|
||||
done();
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
|
||||
|
||||
it("can receive a PreKeySignalMessage",
|
||||
mock.initConverseWithPromises(
|
||||
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
|
||||
function (done, _converse) {
|
||||
|
||||
_converse.NUM_PREKEYS = 5; // Restrict to 5, otherwise the resulting stanza is too large to easily test
|
||||
let view, sent_stanza;
|
||||
test_utils.createContacts(_converse, 'current', 1);
|
||||
_converse.emit('rosterContactsFetched');
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
|
||||
return test_utils.waitUntil(() => initializedOMEMO(_converse))
|
||||
.then(() => _converse.ChatBox.prototype.encryptMessage('This is an encrypted message from the contact'))
|
||||
.then(obj => {
|
||||
// XXX: Normally the key will be encrypted via libsignal.
|
||||
// However, we're mocking libsignal in the tests, so we include
|
||||
// it as plaintext in the message.
|
||||
const key = btoa(JSON.stringify({
|
||||
'type': 1,
|
||||
'body': obj.key_and_tag,
|
||||
'registrationId': '1337'
|
||||
}));
|
||||
const stanza = $msg({
|
||||
'from': contact_jid,
|
||||
'to': _converse.connection.jid,
|
||||
'type': 'chat',
|
||||
'id': 'qwerty'
|
||||
}).c('body').t('This is a fallback message').up()
|
||||
.c('encrypted', {'xmlns': Strophe.NS.OMEMO})
|
||||
.c('header', {'sid': '555'})
|
||||
.c('key', {'prekey': 'true', 'rid': _converse.omemo_store.get('device_id')}).t(key).up()
|
||||
.c('iv').t(obj.iv)
|
||||
.up().up()
|
||||
.c('payload').t(obj.payload);
|
||||
|
||||
const generateMissingPreKeys = _converse.omemo_store.generateMissingPreKeys;
|
||||
spyOn(_converse.omemo_store, 'generateMissingPreKeys').and.callFake(() => {
|
||||
// Since it's difficult to override
|
||||
// decryptPreKeyWhisperMessage, where a prekey will be
|
||||
// removed from the store, we do it here, before the
|
||||
// missing prekeys are generated.
|
||||
_converse.omemo_store.removePreKey(1);
|
||||
return generateMissingPreKeys.apply(_converse.omemo_store, arguments);
|
||||
});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
return test_utils.waitUntil(() => _converse.chatboxviews.get(contact_jid))
|
||||
}).then(iq_stanza => deviceListFetched(_converse, contact_jid))
|
||||
.then(iq_stanza => {
|
||||
const stanza = $iq({
|
||||
'from': contact_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.connection.jid,
|
||||
'type': 'result',
|
||||
}).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
|
||||
.c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
|
||||
.c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
|
||||
.c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
|
||||
.c('device', {'id': '555'});
|
||||
|
||||
// XXX: the bundle gets published twice, we want to make sure
|
||||
// that we wait for the 2nd, so we clear all the already sent
|
||||
// stanzas.
|
||||
_converse.connection.IQ_stanzas = [];
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
return test_utils.waitUntil(() => _converse.omemo_store);
|
||||
}).then(() => test_utils.waitUntil(() => bundleHasBeenPublished(_converse)))
|
||||
.then(iq_stanza => {
|
||||
expect(iq_stanza.outerHTML).toBe(
|
||||
`<iq from="dummy@localhost" type="set" xmlns="jabber:client" id="${iq_stanza.getAttribute('id')}">`+
|
||||
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
||||
`<publish node="eu.siacs.conversations.axolotl.bundles:123456789">`+
|
||||
`<item>`+
|
||||
`<bundle xmlns="eu.siacs.conversations.axolotl">`+
|
||||
`<signedPreKeyPublic signedPreKeyId="0">${btoa('1234')}</signedPreKeyPublic>`+
|
||||
`<signedPreKeySignature>${btoa('11112222333344445555')}</signedPreKeySignature>`+
|
||||
`<identityKey>${btoa('1234')}</identityKey>`+
|
||||
`<prekeys>`+
|
||||
`<preKeyPublic preKeyId="0">${btoa('1234')}</preKeyPublic>`+
|
||||
`<preKeyPublic preKeyId="1">${btoa('1234')}</preKeyPublic>`+
|
||||
`<preKeyPublic preKeyId="2">${btoa('1234')}</preKeyPublic>`+
|
||||
`<preKeyPublic preKeyId="3">${btoa('1234')}</preKeyPublic>`+
|
||||
`<preKeyPublic preKeyId="4">${btoa('1234')}</preKeyPublic>`+
|
||||
`</prekeys>`+
|
||||
`</bundle>`+
|
||||
`</item>`+
|
||||
`</publish>`+
|
||||
`</pubsub>`+
|
||||
`</iq>`)
|
||||
const own_device = _converse.devicelists.get(_converse.bare_jid).devices.get(_converse.omemo_store.get('device_id'));
|
||||
expect(own_device.get('bundle').prekeys.length).toBe(5);
|
||||
expect(_converse.omemo_store.generateMissingPreKeys).toHaveBeenCalled();
|
||||
done();
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
|
||||
|
||||
it("updates device lists based on PEP messages",
|
||||
mock.initConverseWithPromises(
|
||||
null, ['rosterGroupsFetched'], {},
|
||||
function (done, _converse) {
|
||||
|
||||
test_utils.createContacts(_converse, 'current', 1);
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
|
||||
// Wait until own devices are fetched
|
||||
test_utils.waitUntil(() => deviceListFetched(_converse, _converse.bare_jid))
|
||||
.then(iq_stanza => {
|
||||
expect(iq_stanza.outerHTML).toBe(
|
||||
'<iq type="get" from="dummy@localhost" to="dummy@localhost" xmlns="jabber:client" id="'+iq_stanza.getAttribute("id")+'">'+
|
||||
'<pubsub xmlns="http://jabber.org/protocol/pubsub">'+
|
||||
'<items node="eu.siacs.conversations.axolotl.devicelist"/>'+
|
||||
'</pubsub>'+
|
||||
'</iq>');
|
||||
|
||||
const stanza = $iq({
|
||||
'from': _converse.bare_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result',
|
||||
}).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
|
||||
.c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
|
||||
.c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
|
||||
.c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
|
||||
.c('device', {'id': '555'});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
return test_utils.waitUntil(() => _converse.omemo_store);
|
||||
}).then(() => {
|
||||
expect(_converse.devicelists.length).toBe(1);
|
||||
const devicelist = _converse.devicelists.get(_converse.bare_jid);
|
||||
expect(devicelist.devices.length).toBe(2);
|
||||
expect(devicelist.devices.at(0).get('id')).toBe('555');
|
||||
expect(devicelist.devices.at(1).get('id')).toBe('123456789');
|
||||
return test_utils.waitUntil(() => ownDeviceHasBeenPublished(_converse));
|
||||
}).then(iq_stanza => {
|
||||
const stanza = $iq({
|
||||
'from': _converse.bare_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result'});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
return test_utils.waitUntil(() => bundleHasBeenPublished(_converse));
|
||||
}).then(iq_stanza => {
|
||||
const stanza = $iq({
|
||||
'from': _converse.bare_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result'});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
return _converse.api.waitUntil('OMEMOInitialized');
|
||||
}).then(() => {
|
||||
let stanza = $msg({
|
||||
'from': contact_jid,
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'headline',
|
||||
'id': 'update_01',
|
||||
}).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
|
||||
.c('items', {'node': 'eu.siacs.conversations.axolotl.devicelist'})
|
||||
.c('item')
|
||||
.c('list', {'xmlns': 'eu.siacs.conversations.axolotl'})
|
||||
.c('device', {'id': '1234'})
|
||||
.c('device', {'id': '4223'})
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
|
||||
expect(_converse.devicelists.length).toBe(2);
|
||||
let devices = _converse.devicelists.get(contact_jid).devices;
|
||||
expect(devices.length).toBe(2);
|
||||
expect(_.map(devices.models, 'attributes.id').sort().join()).toBe('1234,4223');
|
||||
|
||||
stanza = $msg({
|
||||
'from': contact_jid,
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'headline',
|
||||
'id': 'update_02',
|
||||
}).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
|
||||
.c('items', {'node': 'eu.siacs.conversations.axolotl.devicelist'})
|
||||
.c('item')
|
||||
.c('list', {'xmlns': 'eu.siacs.conversations.axolotl'})
|
||||
.c('device', {'id': '4223'})
|
||||
.c('device', {'id': '4224'})
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
|
||||
expect(_converse.devicelists.length).toBe(2);
|
||||
expect(devices.length).toBe(2);
|
||||
expect(_.map(devices.models, 'attributes.id').sort().join()).toBe('4223,4224');
|
||||
|
||||
// Check that own devicelist gets updated
|
||||
stanza = $msg({
|
||||
'from': _converse.bare_jid,
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'headline',
|
||||
'id': 'update_03',
|
||||
}).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
|
||||
.c('items', {'node': 'eu.siacs.conversations.axolotl.devicelist'})
|
||||
.c('item')
|
||||
.c('list', {'xmlns': 'eu.siacs.conversations.axolotl'})
|
||||
.c('device', {'id': '123456789'})
|
||||
.c('device', {'id': '555'})
|
||||
.c('device', {'id': '777'})
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
|
||||
expect(_converse.devicelists.length).toBe(2);
|
||||
devices = _converse.devicelists.get(_converse.bare_jid).devices;
|
||||
expect(devices.length).toBe(3);
|
||||
expect(_.map(devices.models, 'attributes.id').sort().join()).toBe('123456789,555,777');
|
||||
|
||||
_converse.connection.IQ_stanzas = [];
|
||||
|
||||
// Check that own device gets re-added
|
||||
stanza = $msg({
|
||||
'from': _converse.bare_jid,
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'headline',
|
||||
'id': 'update_04',
|
||||
}).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
|
||||
.c('items', {'node': 'eu.siacs.conversations.axolotl.devicelist'})
|
||||
.c('item')
|
||||
.c('list', {'xmlns': 'eu.siacs.conversations.axolotl'})
|
||||
.c('device', {'id': '444'})
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
|
||||
return test_utils.waitUntil(() => ownDeviceHasBeenPublished(_converse));
|
||||
}).then(iq_stanza => {
|
||||
// Check that our own device is added again, but that removed
|
||||
// devices are not added.
|
||||
expect(iq_stanza.outerHTML).toBe(
|
||||
'<iq from="dummy@localhost" type="set" xmlns="jabber:client" id="'+iq_stanza.getAttribute('id')+'">'+
|
||||
'<pubsub xmlns="http://jabber.org/protocol/pubsub">'+
|
||||
'<publish node="eu.siacs.conversations.axolotl.devicelist">'+
|
||||
'<item>'+
|
||||
'<list xmlns="eu.siacs.conversations.axolotl">'+
|
||||
'<device id="123456789"/>'+
|
||||
'<device id="444"/>'+
|
||||
'</list>'+
|
||||
'</item>'+
|
||||
'</publish>'+
|
||||
'</pubsub>'+
|
||||
'</iq>');
|
||||
expect(_converse.devicelists.length).toBe(2);
|
||||
const devices = _converse.devicelists.get(_converse.bare_jid).devices;
|
||||
// The device id for this device (123456789) was also generated and added to the list,
|
||||
// which is why we have 2 devices now.
|
||||
expect(devices.length).toBe(2);
|
||||
expect(_.map(devices.models, 'attributes.id').sort().join()).toBe('123456789,444');
|
||||
done();
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
|
||||
|
||||
it("updates device bundles based on PEP messages",
|
||||
mock.initConverseWithPromises(
|
||||
null, ['rosterGroupsFetched'], {},
|
||||
function (done, _converse) {
|
||||
|
||||
test_utils.createContacts(_converse, 'current');
|
||||
const contact_jid = mock.cur_names[3].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
|
||||
test_utils.waitUntil(() => deviceListFetched(_converse, _converse.bare_jid))
|
||||
.then(iq_stanza => {
|
||||
expect(iq_stanza.outerHTML).toBe(
|
||||
'<iq type="get" from="dummy@localhost" to="dummy@localhost" xmlns="jabber:client" id="'+iq_stanza.getAttribute("id")+'">'+
|
||||
'<pubsub xmlns="http://jabber.org/protocol/pubsub">'+
|
||||
'<items node="eu.siacs.conversations.axolotl.devicelist"/>'+
|
||||
'</pubsub>'+
|
||||
'</iq>');
|
||||
|
||||
const stanza = $iq({
|
||||
'from': contact_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result',
|
||||
}).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
|
||||
.c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
|
||||
.c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
|
||||
.c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
|
||||
.c('device', {'id': '555'});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
return test_utils.waitUntil(() => _converse.omemo_store);
|
||||
}).then(() => {
|
||||
expect(_converse.devicelists.length).toBe(1);
|
||||
const devicelist = _converse.devicelists.get(_converse.bare_jid);
|
||||
expect(devicelist.devices.length).toBe(2);
|
||||
expect(devicelist.devices.at(0).get('id')).toBe('555');
|
||||
expect(devicelist.devices.at(1).get('id')).toBe('123456789');
|
||||
return test_utils.waitUntil(() => ownDeviceHasBeenPublished(_converse));
|
||||
}).then(iq_stanza => {
|
||||
const stanza = $iq({
|
||||
'from': _converse.bare_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result'});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
return test_utils.waitUntil(() => bundleHasBeenPublished(_converse));
|
||||
}).then(iq_stanza => {
|
||||
const stanza = $iq({
|
||||
'from': _converse.bare_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result'});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
return _converse.api.waitUntil('OMEMOInitialized');
|
||||
}).then(() => {
|
||||
let stanza = $msg({
|
||||
'from': contact_jid,
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'headline',
|
||||
'id': 'update_01',
|
||||
}).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
|
||||
.c('items', {'node': 'eu.siacs.conversations.axolotl.bundles:555'})
|
||||
.c('item')
|
||||
.c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
|
||||
.c('signedPreKeyPublic', {'signedPreKeyId': '4223'}).t('1111').up()
|
||||
.c('signedPreKeySignature').t('2222').up()
|
||||
.c('identityKey').t('3333').up()
|
||||
.c('prekeys')
|
||||
.c('preKeyPublic', {'preKeyId': '1001'}).up()
|
||||
.c('preKeyPublic', {'preKeyId': '1002'}).up()
|
||||
.c('preKeyPublic', {'preKeyId': '1003'});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
|
||||
expect(_converse.devicelists.length).toBe(2);
|
||||
let devicelist = _converse.devicelists.get(contact_jid);
|
||||
expect(devicelist.devices.length).toBe(1);
|
||||
let device = devicelist.devices.at(0);
|
||||
expect(device.get('bundle').identity_key).toBe('3333');
|
||||
expect(device.get('bundle').signed_prekey.public_key).toBe('1111');
|
||||
expect(device.get('bundle').signed_prekey.id).toBe(4223);
|
||||
expect(device.get('bundle').signed_prekey.signature).toBe('2222');
|
||||
expect(device.get('bundle').prekeys.length).toBe(3);
|
||||
expect(device.get('bundle').prekeys[0].id).toBe(1001);
|
||||
expect(device.get('bundle').prekeys[1].id).toBe(1002);
|
||||
expect(device.get('bundle').prekeys[2].id).toBe(1003);
|
||||
|
||||
stanza = $msg({
|
||||
'from': contact_jid,
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'headline',
|
||||
'id': 'update_02',
|
||||
}).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
|
||||
.c('items', {'node': 'eu.siacs.conversations.axolotl.bundles:555'})
|
||||
.c('item')
|
||||
.c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
|
||||
.c('signedPreKeyPublic', {'signedPreKeyId': '4223'}).t('5555').up()
|
||||
.c('signedPreKeySignature').t('6666').up()
|
||||
.c('identityKey').t('7777').up()
|
||||
.c('prekeys')
|
||||
.c('preKeyPublic', {'preKeyId': '2001'}).up()
|
||||
.c('preKeyPublic', {'preKeyId': '2002'}).up()
|
||||
.c('preKeyPublic', {'preKeyId': '2003'});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
|
||||
expect(_converse.devicelists.length).toBe(2);
|
||||
devicelist = _converse.devicelists.get(contact_jid);
|
||||
expect(devicelist.devices.length).toBe(1);
|
||||
device = devicelist.devices.at(0);
|
||||
expect(device.get('bundle').identity_key).toBe('7777');
|
||||
expect(device.get('bundle').signed_prekey.public_key).toBe('5555');
|
||||
expect(device.get('bundle').signed_prekey.id).toBe(4223);
|
||||
expect(device.get('bundle').signed_prekey.signature).toBe('6666');
|
||||
expect(device.get('bundle').prekeys.length).toBe(3);
|
||||
expect(device.get('bundle').prekeys[0].id).toBe(2001);
|
||||
expect(device.get('bundle').prekeys[1].id).toBe(2002);
|
||||
expect(device.get('bundle').prekeys[2].id).toBe(2003);
|
||||
|
||||
stanza = $msg({
|
||||
'from': _converse.bare_jid,
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'headline',
|
||||
'id': 'update_03',
|
||||
}).c('event', {'xmlns': 'http://jabber.org/protocol/pubsub#event'})
|
||||
.c('items', {'node': 'eu.siacs.conversations.axolotl.bundles:123456789'})
|
||||
.c('item')
|
||||
.c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
|
||||
.c('signedPreKeyPublic', {'signedPreKeyId': '9999'}).t('8888').up()
|
||||
.c('signedPreKeySignature').t('3333').up()
|
||||
.c('identityKey').t('1111').up()
|
||||
.c('prekeys')
|
||||
.c('preKeyPublic', {'preKeyId': '3001'}).up()
|
||||
.c('preKeyPublic', {'preKeyId': '3002'}).up()
|
||||
.c('preKeyPublic', {'preKeyId': '3003'});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
|
||||
expect(_converse.devicelists.length).toBe(2);
|
||||
devicelist = _converse.devicelists.get(_converse.bare_jid);
|
||||
expect(devicelist.devices.length).toBe(2);
|
||||
expect(devicelist.devices.at(0).get('id')).toBe('555');
|
||||
expect(devicelist.devices.at(1).get('id')).toBe('123456789');
|
||||
device = devicelist.devices.at(1);
|
||||
expect(device.get('bundle').identity_key).toBe('1111');
|
||||
expect(device.get('bundle').signed_prekey.public_key).toBe('8888');
|
||||
expect(device.get('bundle').signed_prekey.id).toBe(9999);
|
||||
expect(device.get('bundle').signed_prekey.signature).toBe('3333');
|
||||
expect(device.get('bundle').prekeys.length).toBe(3);
|
||||
expect(device.get('bundle').prekeys[0].id).toBe(3001);
|
||||
expect(device.get('bundle').prekeys[1].id).toBe(3002);
|
||||
expect(device.get('bundle').prekeys[2].id).toBe(3003);
|
||||
done();
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
|
||||
it("publishes a bundle with which an encrypted session can be created",
|
||||
mock.initConverseWithPromises(
|
||||
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
|
||||
function (done, _converse) {
|
||||
|
||||
_converse.NUM_PREKEYS = 2; // Restrict to 2, otherwise the resulting stanza is too large to easily test
|
||||
|
||||
test_utils.createContacts(_converse, 'current', 1);
|
||||
_converse.emit('rosterContactsFetched');
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
|
||||
test_utils.waitUntil(() => deviceListFetched(_converse, _converse.bare_jid))
|
||||
.then(iq_stanza => {
|
||||
const stanza = $iq({
|
||||
'from': contact_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result',
|
||||
}).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
|
||||
.c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
|
||||
.c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
|
||||
.c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
|
||||
.c('device', {'id': '482886413b977930064a5888b92134fe'});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
expect(_converse.devicelists.length).toBe(1);
|
||||
return test_utils.openChatBoxFor(_converse, contact_jid);
|
||||
|
||||
}).then(() => ownDeviceHasBeenPublished(_converse))
|
||||
.then(iq_stanza => {
|
||||
const stanza = $iq({
|
||||
'from': _converse.bare_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result'});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
|
||||
return test_utils.waitUntil(() => bundleHasBeenPublished(_converse));
|
||||
}).then(iq_stanza => {
|
||||
expect(iq_stanza.outerHTML).toBe(
|
||||
`<iq from="dummy@localhost" type="set" xmlns="jabber:client" id="${iq_stanza.getAttribute('id')}">`+
|
||||
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
||||
`<publish node="eu.siacs.conversations.axolotl.bundles:123456789">`+
|
||||
`<item>`+
|
||||
`<bundle xmlns="eu.siacs.conversations.axolotl">`+
|
||||
`<signedPreKeyPublic signedPreKeyId="0">${btoa('1234')}</signedPreKeyPublic>`+
|
||||
`<signedPreKeySignature>${btoa('11112222333344445555')}</signedPreKeySignature>`+
|
||||
`<identityKey>${btoa('1234')}</identityKey>`+
|
||||
`<prekeys>`+
|
||||
`<preKeyPublic preKeyId="0">${btoa('1234')}</preKeyPublic>`+
|
||||
`<preKeyPublic preKeyId="1">${btoa('1234')}</preKeyPublic>`+
|
||||
`</prekeys>`+
|
||||
`</bundle>`+
|
||||
`</item>`+
|
||||
`</publish>`+
|
||||
`</pubsub>`+
|
||||
`</iq>`)
|
||||
|
||||
const stanza = $iq({
|
||||
'from': _converse.bare_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result'});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
return _converse.api.waitUntil('OMEMOInitialized');
|
||||
}).then(done).catch(_.partial(console.error, _));
|
||||
}));
|
||||
|
||||
|
||||
it("adds a toolbar button for starting an encrypted chat session",
|
||||
mock.initConverseWithPromises(
|
||||
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
|
||||
function (done, _converse) {
|
||||
|
||||
let modal;
|
||||
test_utils.createContacts(_converse, 'current', 1);
|
||||
_converse.emit('rosterContactsFetched');
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
|
||||
test_utils.waitUntil(() => deviceListFetched(_converse, _converse.bare_jid))
|
||||
.then(iq_stanza => {
|
||||
expect(iq_stanza.outerHTML).toBe(
|
||||
'<iq type="get" from="dummy@localhost" to="dummy@localhost" xmlns="jabber:client" id="'+iq_stanza.getAttribute("id")+'">'+
|
||||
'<pubsub xmlns="http://jabber.org/protocol/pubsub">'+
|
||||
'<items node="eu.siacs.conversations.axolotl.devicelist"/>'+
|
||||
'</pubsub>'+
|
||||
'</iq>');
|
||||
|
||||
const stanza = $iq({
|
||||
'from': _converse.bare_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result',
|
||||
}).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
|
||||
.c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
|
||||
.c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
|
||||
.c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
|
||||
.c('device', {'id': '482886413b977930064a5888b92134fe'});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
return test_utils.waitUntil(() => _converse.omemo_store);
|
||||
}).then(() => {
|
||||
expect(_converse.devicelists.length).toBe(1);
|
||||
const devicelist = _converse.devicelists.get(_converse.bare_jid);
|
||||
expect(devicelist.devices.length).toBe(2);
|
||||
expect(devicelist.devices.at(0).get('id')).toBe('482886413b977930064a5888b92134fe');
|
||||
expect(devicelist.devices.at(1).get('id')).toBe('123456789');
|
||||
// Check that own device was published
|
||||
return test_utils.waitUntil(() => ownDeviceHasBeenPublished(_converse));
|
||||
}).then(iq_stanza => {
|
||||
expect(iq_stanza.outerHTML).toBe(
|
||||
'<iq from="dummy@localhost" type="set" xmlns="jabber:client" id="'+iq_stanza.getAttribute('id')+'">'+
|
||||
'<pubsub xmlns="http://jabber.org/protocol/pubsub">'+
|
||||
'<publish node="eu.siacs.conversations.axolotl.devicelist">'+
|
||||
'<item>'+
|
||||
'<list xmlns="eu.siacs.conversations.axolotl">'+
|
||||
'<device id="482886413b977930064a5888b92134fe"/>'+
|
||||
'<device id="123456789"/>'+
|
||||
'</list>'+
|
||||
'</item>'+
|
||||
'</publish>'+
|
||||
'</pubsub>'+
|
||||
'</iq>');
|
||||
|
||||
const stanza = $iq({
|
||||
'from': _converse.bare_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result'});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
|
||||
return test_utils.waitUntil(() => bundleHasBeenPublished(_converse));
|
||||
}).then(iq_stanza => {
|
||||
expect(iq_stanza.getAttributeNames().sort().join()).toBe(["from", "type", "xmlns", "id"].sort().join());
|
||||
expect(iq_stanza.querySelector('prekeys').childNodes.length).toBe(100);
|
||||
|
||||
const signed_prekeys = iq_stanza.querySelectorAll('signedPreKeyPublic');
|
||||
expect(signed_prekeys.length).toBe(1);
|
||||
const signed_prekey = signed_prekeys[0];
|
||||
expect(signed_prekey.getAttribute('signedPreKeyId')).toBe('0')
|
||||
expect(iq_stanza.querySelectorAll('signedPreKeySignature').length).toBe(1);
|
||||
expect(iq_stanza.querySelectorAll('identityKey').length).toBe(1);
|
||||
|
||||
const stanza = $iq({
|
||||
'from': _converse.bare_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result'});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
return _converse.api.waitUntil('OMEMOInitialized', 1000);
|
||||
}).then(() => {
|
||||
return test_utils.openChatBoxFor(_converse, contact_jid);
|
||||
}).then(() => {
|
||||
return test_utils.waitUntil(() => deviceListFetched(_converse, contact_jid));
|
||||
}).then(iq_stanza => {
|
||||
expect(iq_stanza.outerHTML).toBe(
|
||||
'<iq type="get" from="dummy@localhost" to="'+contact_jid+'" xmlns="jabber:client" id="'+iq_stanza.getAttribute("id")+'">'+
|
||||
'<pubsub xmlns="http://jabber.org/protocol/pubsub">'+
|
||||
'<items node="eu.siacs.conversations.axolotl.devicelist"/>'+
|
||||
'</pubsub>'+
|
||||
'</iq>');
|
||||
|
||||
const stanza = $iq({
|
||||
'from': contact_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result',
|
||||
}).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
|
||||
.c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
|
||||
.c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
|
||||
.c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
|
||||
.c('device', {'id': '368866411b877c30064a5f62b917cffe'}).up()
|
||||
.c('device', {'id': '3300659945416e274474e469a1f0154c'}).up()
|
||||
.c('device', {'id': '4e30f35051b7b8b42abe083742187228'}).up()
|
||||
.c('device', {'id': 'ae890ac52d0df67ed7cfdf51b644e901'});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
const devicelist = _converse.devicelists.get(contact_jid);
|
||||
return test_utils.waitUntil(() => devicelist.devices.length);
|
||||
}).then(() => {
|
||||
expect(_converse.devicelists.length).toBe(2);
|
||||
const devicelist = _converse.devicelists.get(contact_jid);
|
||||
expect(devicelist.devices.length).toBe(4);
|
||||
expect(devicelist.devices.at(0).get('id')).toBe('368866411b877c30064a5f62b917cffe');
|
||||
expect(devicelist.devices.at(1).get('id')).toBe('3300659945416e274474e469a1f0154c');
|
||||
expect(devicelist.devices.at(2).get('id')).toBe('4e30f35051b7b8b42abe083742187228');
|
||||
expect(devicelist.devices.at(3).get('id')).toBe('ae890ac52d0df67ed7cfdf51b644e901');
|
||||
return test_utils.waitUntil(() => _converse.chatboxviews.get(contact_jid).el.querySelector('.chat-toolbar'));
|
||||
}).then(() => {
|
||||
const view = _converse.chatboxviews.get(contact_jid);
|
||||
const toolbar = view.el.querySelector('.chat-toolbar');
|
||||
expect(view.model.get('omemo_active')).toBe(undefined);
|
||||
const toggle = toolbar.querySelector('.toggle-omemo');
|
||||
expect(_.isNull(toggle)).toBe(false);
|
||||
expect(u.hasClass('fa-unlock', toggle)).toBe(true);
|
||||
expect(u.hasClass('fa-lock', toggle)).toBe(false);
|
||||
|
||||
spyOn(view, 'toggleOMEMO').and.callThrough();
|
||||
view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
|
||||
toolbar.querySelector('.toggle-omemo').click();
|
||||
expect(view.toggleOMEMO).toHaveBeenCalled();
|
||||
expect(view.model.get('omemo_active')).toBe(true);
|
||||
|
||||
return test_utils.waitUntil(() => u.hasClass('fa-lock', toolbar.querySelector('.toggle-omemo')));
|
||||
}).then(() => {
|
||||
const view = _converse.chatboxviews.get(contact_jid);
|
||||
const toolbar = view.el.querySelector('.chat-toolbar');
|
||||
const toggle = toolbar.querySelector('.toggle-omemo');
|
||||
expect(u.hasClass('fa-unlock', toggle)).toBe(false);
|
||||
expect(u.hasClass('fa-lock', toggle)).toBe(true);
|
||||
|
||||
const textarea = view.el.querySelector('.chat-textarea');
|
||||
textarea.value = 'This message will be sent encrypted';
|
||||
view.keyPressed({
|
||||
target: textarea,
|
||||
preventDefault: _.noop,
|
||||
keyCode: 13
|
||||
});
|
||||
done();
|
||||
}).catch(_.partial(console.error, _));
|
||||
}));
|
||||
|
||||
|
||||
it("shows OMEMO device fingerprints in the user details modal",
|
||||
mock.initConverseWithPromises(
|
||||
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
|
||||
function (done, _converse) {
|
||||
|
||||
let modal;
|
||||
test_utils.createContacts(_converse, 'current', 1);
|
||||
_converse.emit('rosterContactsFetched');
|
||||
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
test_utils.openChatBoxFor(_converse, contact_jid)
|
||||
.then(() => {
|
||||
// We simply emit, to avoid doing all the setup work
|
||||
_converse.emit('OMEMOInitialized');
|
||||
|
||||
const view = _converse.chatboxviews.get(contact_jid);
|
||||
const show_modal_button = view.el.querySelector('.show-user-details-modal');
|
||||
show_modal_button.click();
|
||||
modal = view.user_details_modal;
|
||||
|
||||
return test_utils.waitUntil(() => u.isVisible(modal.el), 1000);
|
||||
}).then(() => test_utils.waitUntil(() => deviceListFetched(_converse, contact_jid)))
|
||||
.then(iq_stanza => {
|
||||
expect(iq_stanza.outerHTML).toBe(
|
||||
`<iq type="get" from="dummy@localhost" to="max.frankfurter@localhost" xmlns="jabber:client" id="${iq_stanza.getAttribute('id')}">`+
|
||||
`<pubsub xmlns="http://jabber.org/protocol/pubsub"><items node="eu.siacs.conversations.axolotl.devicelist"/></pubsub>`+
|
||||
`</iq>`);
|
||||
|
||||
const stanza = $iq({
|
||||
'from': contact_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result',
|
||||
}).c('pubsub', {'xmlns': "http://jabber.org/protocol/pubsub"})
|
||||
.c('items', {'node': "eu.siacs.conversations.axolotl.devicelist"})
|
||||
.c('item', {'xmlns': "http://jabber.org/protocol/pubsub"}) // TODO: must have an id attribute
|
||||
.c('list', {'xmlns': "eu.siacs.conversations.axolotl"})
|
||||
.c('device', {'id': '555'});
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
|
||||
return test_utils.waitUntil(() => u.isVisible(modal.el), 1000);
|
||||
}).then(() => test_utils.waitUntil(() => bundleFetched(_converse, contact_jid, '555')))
|
||||
.then(iq_stanza => {
|
||||
expect(iq_stanza.outerHTML).toBe(
|
||||
`<iq type="get" from="dummy@localhost" to="max.frankfurter@localhost" xmlns="jabber:client" id="${iq_stanza.getAttribute('id')}">`+
|
||||
`<pubsub xmlns="http://jabber.org/protocol/pubsub">`+
|
||||
`<items node="eu.siacs.conversations.axolotl.bundles:555"/>`+
|
||||
`</pubsub>`+
|
||||
`</iq>`);
|
||||
|
||||
const stanza = $iq({
|
||||
'from': contact_jid,
|
||||
'id': iq_stanza.getAttribute('id'),
|
||||
'to': _converse.bare_jid,
|
||||
'type': 'result',
|
||||
}).c('pubsub', {
|
||||
'xmlns': 'http://jabber.org/protocol/pubsub'
|
||||
}).c('items', {'node': "eu.siacs.conversations.axolotl.bundles:555"})
|
||||
.c('item')
|
||||
.c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
|
||||
.c('signedPreKeyPublic', {'signedPreKeyId': '4223'}).t(btoa('1111')).up()
|
||||
.c('signedPreKeySignature').t(btoa('2222')).up()
|
||||
.c('identityKey').t('BQmHEOHjsYm3w5M8VqxAtqJmLCi7CaxxsdZz6G0YpuMI').up()
|
||||
.c('prekeys')
|
||||
.c('preKeyPublic', {'preKeyId': '1'}).t(btoa('1001')).up()
|
||||
.c('preKeyPublic', {'preKeyId': '2'}).t(btoa('1002')).up()
|
||||
.c('preKeyPublic', {'preKeyId': '3'}).t(btoa('1003'));
|
||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||
|
||||
const view = _converse.chatboxviews.get(contact_jid);
|
||||
const modal = view.user_details_modal;
|
||||
return test_utils.waitUntil(() => modal.el.querySelectorAll('.fingerprints .fingerprint').length);
|
||||
}).then(() => {
|
||||
const view = _converse.chatboxviews.get(contact_jid);
|
||||
const modal = view.user_details_modal;
|
||||
expect(modal.el.querySelectorAll('.fingerprints .fingerprint').length).toBe(1);
|
||||
const el = modal.el.querySelector('.fingerprints .fingerprint');
|
||||
expect(el.textContent.trim()).toBe(
|
||||
u.formatFingerprint(u.arrayBufferToHex(u.base64ToArrayBuffer('BQmHEOHjsYm3w5M8VqxAtqJmLCi7CaxxsdZz6G0YpuMI')))
|
||||
);
|
||||
|
||||
expect(modal.el.querySelectorAll('input[type="radio"]').length).toBe(2);
|
||||
|
||||
const devicelist = _converse.devicelists.get(contact_jid);
|
||||
expect(devicelist.devices.get('555').get('trusted')).toBe(0);
|
||||
|
||||
let trusted_radio = modal.el.querySelector('input[type="radio"][name="555"][value="1"]');
|
||||
expect(trusted_radio.checked).toBe(true);
|
||||
|
||||
let untrusted_radio = modal.el.querySelector('input[type="radio"][name="555"][value="-1"]');
|
||||
expect(untrusted_radio.checked).toBe(false);
|
||||
|
||||
// Test that the device can be set to untrusted
|
||||
untrusted_radio.click();
|
||||
trusted_radio = document.querySelector('input[type="radio"][name="555"][value="1"]');
|
||||
expect(trusted_radio.hasAttribute('checked')).toBe(false);
|
||||
expect(devicelist.devices.get('555').get('trusted')).toBe(-1);
|
||||
|
||||
untrusted_radio = document.querySelector('input[type="radio"][name="555"][value="-1"]');
|
||||
expect(untrusted_radio.hasAttribute('checked')).toBe(true);
|
||||
|
||||
trusted_radio.click();
|
||||
expect(devicelist.devices.get('555').get('trusted')).toBe(1);
|
||||
done();
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL))
|
||||
}));
|
||||
});
|
||||
|
||||
describe("A chatbox with an active OMEMO session", function() {
|
||||
|
||||
it("will not show the spoiler toolbar button",
|
||||
mock.initConverseWithPromises(
|
||||
null, ['rosterGroupsFetched'], {},
|
||||
function (done, _converse) {
|
||||
// TODO
|
||||
done()
|
||||
}));
|
||||
});
|
||||
}));
|
@ -47,7 +47,7 @@
|
||||
"<presence xmlns='jabber:client'>"+
|
||||
"<status>Hello world</status>"+
|
||||
"<priority>0</priority>"+
|
||||
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='wmJWAEmiBuDhg0VUoDmqHp3qXJ0='/>"+
|
||||
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='ggltNSI5YG/7dFKB57Bk2dRYRU0='/>"+
|
||||
"</presence>"
|
||||
);
|
||||
_converse.priority = 2;
|
||||
@ -57,7 +57,7 @@
|
||||
"<show>away</show>"+
|
||||
"<status>Going jogging</status>"+
|
||||
"<priority>2</priority>"+
|
||||
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='wmJWAEmiBuDhg0VUoDmqHp3qXJ0='/>"+
|
||||
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='ggltNSI5YG/7dFKB57Bk2dRYRU0='/>"+
|
||||
"</presence>"
|
||||
);
|
||||
|
||||
@ -68,7 +68,7 @@
|
||||
"<show>dnd</show>"+
|
||||
"<status>Doing taxes</status>"+
|
||||
"<priority>0</priority>"+
|
||||
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='wmJWAEmiBuDhg0VUoDmqHp3qXJ0='/>"+
|
||||
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='ggltNSI5YG/7dFKB57Bk2dRYRU0='/>"+
|
||||
"</presence>"
|
||||
);
|
||||
}));
|
||||
@ -97,7 +97,7 @@
|
||||
.toBe("<presence xmlns='jabber:client'>"+
|
||||
"<status>My custom status</status>"+
|
||||
"<priority>0</priority>"+
|
||||
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='wmJWAEmiBuDhg0VUoDmqHp3qXJ0='/>"+
|
||||
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='ggltNSI5YG/7dFKB57Bk2dRYRU0='/>"+
|
||||
"</presence>")
|
||||
|
||||
return test_utils.waitUntil(function () {
|
||||
@ -113,7 +113,7 @@
|
||||
modal.el.querySelector('[type="submit"]').click();
|
||||
expect(_converse.connection.send.calls.mostRecent().args[0].toLocaleString())
|
||||
.toBe("<presence xmlns='jabber:client'><show>dnd</show><status>My custom status</status><priority>0</priority>"+
|
||||
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='wmJWAEmiBuDhg0VUoDmqHp3qXJ0='/>"+
|
||||
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='ggltNSI5YG/7dFKB57Bk2dRYRU0='/>"+
|
||||
"</presence>")
|
||||
done();
|
||||
});
|
||||
|
@ -229,14 +229,16 @@
|
||||
// have to mock stanza traffic.
|
||||
}, function (done, _converse) {
|
||||
|
||||
let view, nick;
|
||||
const room_jid = 'kitchen@conference.shakespeare.lit';
|
||||
|
||||
test_utils.waitUntil(() => !_.isUndefined(_converse.rooms_list_view), 500)
|
||||
.then(() => test_utils.openAndEnterChatRoom(_converse, 'kitchen', 'conference.shakespeare.lit', 'romeo'))
|
||||
.then(() => {
|
||||
const room_jid = 'kitchen@conference.shakespeare.lit';
|
||||
const view = _converse.chatboxviews.get(room_jid);
|
||||
view = _converse.chatboxviews.get(room_jid);
|
||||
view.model.set({'minimized': true});
|
||||
const contact_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||
const nick = mock.chatroom_names[0];
|
||||
nick = mock.chatroom_names[0];
|
||||
view.model.onMessage(
|
||||
$msg({
|
||||
from: room_jid+'/'+nick,
|
||||
@ -260,9 +262,11 @@
|
||||
type: 'groupchat'
|
||||
}).c('body').t('romeo: Your attention is required').tree()
|
||||
);
|
||||
var indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
|
||||
return test_utils.waitUntil(() => _converse.rooms_list_view.el.querySelectorAll(".msgs-indicator"));
|
||||
}).then(() => {
|
||||
spyOn(view.model, 'incrementUnreadMsgCounter').and.callThrough();
|
||||
const indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
|
||||
expect(indicator_el.textContent).toBe('1');
|
||||
|
||||
view.model.onMessage(
|
||||
$msg({
|
||||
from: room_jid+'/'+nick,
|
||||
@ -271,14 +275,16 @@
|
||||
type: 'groupchat'
|
||||
}).c('body').t('romeo: and another thing...').tree()
|
||||
);
|
||||
indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
|
||||
return test_utils.waitUntil(() => view.model.incrementUnreadMsgCounter.calls.count());
|
||||
}).then(() => {
|
||||
let indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
|
||||
expect(indicator_el.textContent).toBe('2');
|
||||
|
||||
// When the chat gets maximized again, the unread indicators are removed
|
||||
view.model.set({'minimized': false});
|
||||
indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
|
||||
expect(_.isNull(indicator_el));
|
||||
room_el = _converse.rooms_list_view.el.querySelector(".available-chatroom");
|
||||
const room_el = _converse.rooms_list_view.el.querySelector(".available-chatroom");
|
||||
expect(_.includes(room_el.classList, 'unread-msgs')).toBeFalsy();
|
||||
done();
|
||||
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
|
||||
|
@ -389,7 +389,6 @@
|
||||
function (done, _converse) {
|
||||
|
||||
_converse.roster_groups = true;
|
||||
spyOn(_converse, 'emit');
|
||||
spyOn(_converse.rosterview, 'update').and.callThrough();
|
||||
_converse.rosterview.render();
|
||||
test_utils.openControlBox();
|
||||
@ -430,7 +429,6 @@
|
||||
function (done, _converse) {
|
||||
|
||||
_converse.roster_groups = true;
|
||||
spyOn(_converse, 'emit');
|
||||
spyOn(_converse.rosterview, 'update').and.callThrough();
|
||||
_converse.rosterview.render();
|
||||
|
||||
@ -477,7 +475,6 @@
|
||||
|
||||
_converse.roster_groups = true;
|
||||
var groups = ['colleagues', 'friends'];
|
||||
spyOn(_converse, 'emit');
|
||||
spyOn(_converse.rosterview, 'update').and.callThrough();
|
||||
test_utils.openControlBox();
|
||||
_converse.rosterview.render();
|
||||
@ -576,7 +573,6 @@
|
||||
null, ['rosterGroupsFetched'], {},
|
||||
function (done, _converse) {
|
||||
|
||||
spyOn(_converse, 'emit');
|
||||
spyOn(_converse.rosterview, 'update').and.callThrough();
|
||||
test_utils.openControlBox();
|
||||
_converse.roster.create({
|
||||
@ -726,7 +722,6 @@
|
||||
|
||||
var i, t;
|
||||
test_utils.openControlBox();
|
||||
spyOn(_converse, 'emit');
|
||||
spyOn(_converse.rosterview, 'update').and.callThrough();
|
||||
for (i=0; i<mock.pend_names.length; i++) {
|
||||
_converse.roster.create({
|
||||
@ -908,7 +903,6 @@
|
||||
test_utils.waitUntil(() => $(_converse.rosterview.el).find('.roster-group li').length, 700)
|
||||
.then(function () {
|
||||
var jid, t;
|
||||
spyOn(_converse, 'emit');
|
||||
spyOn(_converse.rosterview, 'update').and.callThrough();
|
||||
var $roster = $(_converse.rosterview.el);
|
||||
for (var i=0; i<mock.cur_names.length; i++) {
|
||||
@ -935,7 +929,6 @@
|
||||
return $(_converse.rosterview.el).find('.roster-group li').length;
|
||||
}, 700).then(function () {
|
||||
var jid, t;
|
||||
spyOn(_converse, 'emit');
|
||||
spyOn(_converse.rosterview, 'update').and.callThrough();
|
||||
var $roster = $(_converse.rosterview.el);
|
||||
for (var i=0; i<mock.cur_names.length; i++) {
|
||||
@ -963,7 +956,6 @@
|
||||
return $(_converse.rosterview.el).find('.roster-group li').length;
|
||||
}, 700).then(function () {
|
||||
var jid, t;
|
||||
spyOn(_converse, 'emit');
|
||||
spyOn(_converse.rosterview, 'update').and.callThrough();
|
||||
var $roster = $(_converse.rosterview.el);
|
||||
for (var i=0; i<mock.cur_names.length; i++) {
|
||||
@ -991,7 +983,6 @@
|
||||
return $(_converse.rosterview.el).find('.roster-group li').length;
|
||||
}, 700).then(function () {
|
||||
var jid, t;
|
||||
spyOn(_converse, 'emit');
|
||||
spyOn(_converse.rosterview, 'update').and.callThrough();
|
||||
var $roster = $(_converse.rosterview.el);
|
||||
for (var i=0; i<mock.cur_names.length; i++) {
|
||||
@ -1020,7 +1011,6 @@
|
||||
}, 500)
|
||||
.then(function () {
|
||||
var jid, t;
|
||||
spyOn(_converse, 'emit');
|
||||
spyOn(_converse.rosterview, 'update').and.callThrough();
|
||||
var $roster = $(_converse.rosterview.el);
|
||||
for (var i=0; i<mock.cur_names.length; i++) {
|
||||
@ -1151,7 +1141,6 @@
|
||||
names.push($(item).text().replace(/^\s+|\s+$/g, ''));
|
||||
}
|
||||
};
|
||||
spyOn(_converse, 'emit');
|
||||
spyOn(_converse.rosterview, 'update').and.callThrough();
|
||||
spyOn(_converse.controlboxtoggle, 'showControlBox').and.callThrough();
|
||||
for (i=0; i<mock.req_names.length; i++) {
|
||||
|
@ -78,8 +78,9 @@
|
||||
remove_contact_button.click();
|
||||
return test_utils.waitUntil(() => u.isVisible(document.querySelector('.alert-danger')), 2000);
|
||||
}).then(function () {
|
||||
expect(document.querySelector('.alert-danger .modal-title').textContent).toBe("Error");
|
||||
expect(document.querySelector('.modal:not(#user-profile-modal) .modal-body p').textContent.trim())
|
||||
const header = document.querySelector('.alert-danger .modal-title');
|
||||
expect(header.textContent).toBe("Error");
|
||||
expect(u.ancestor(header, '.modal-content').querySelector('.modal-body p').textContent.trim())
|
||||
.toBe("Sorry, there was an error while trying to remove Max Frankfurter as a contact.");
|
||||
document.querySelector('.alert-danger button.close').click();
|
||||
const show_modal_button = view.el.querySelector('.show-user-details-modal');
|
||||
|
@ -577,8 +577,10 @@
|
||||
// Add a handler for bookmarks pushed from other connected clients
|
||||
// (from the same user obviously)
|
||||
_converse.connection.addHandler((message) => {
|
||||
if (message.querySelector('event[xmlns="'+Strophe.NS.PUBSUB+'#event"]')) {
|
||||
_converse.bookmarks.createBookmarksFromStanza(message);
|
||||
if (sizzle('event[xmlns="'+Strophe.NS.PUBSUB+'#event"] items[node="storage:bookmarks"]', message).length) {
|
||||
_converse.api.waitUntil('bookmarksInitialized')
|
||||
.then(() => _converse.bookmarks.createBookmarksFromStanza(message))
|
||||
.catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
|
||||
}
|
||||
}, null, 'message', 'headline', null, _converse.bare_jid);
|
||||
});
|
||||
|
@ -265,7 +265,7 @@
|
||||
|
||||
this.messages.on('change:upload', (message) => {
|
||||
if (message.get('upload') === _converse.SUCCESS) {
|
||||
this.sendMessageStanza(message);
|
||||
this.sendMessageStanza(this.createMessageStanza(message));
|
||||
}
|
||||
});
|
||||
|
||||
@ -295,8 +295,16 @@
|
||||
const replace = sizzle(`replace[xmlns="${Strophe.NS.MESSAGE_CORRECT}"]`, stanza).pop();
|
||||
if (replace) {
|
||||
const msgid = replace && replace.getAttribute('id') || stanza.getAttribute('id'),
|
||||
message = msgid && this.messages.findWhere({msgid}),
|
||||
older_versions = message.get('older_versions') || [];
|
||||
message = msgid && this.messages.findWhere({msgid});
|
||||
|
||||
if (!message) {
|
||||
// XXX: Looks like we received a correction for a
|
||||
// non-existing message, probably due to MAM.
|
||||
// Not clear what can be done about this... we'll
|
||||
// just create it as a separate message for now.
|
||||
return false;
|
||||
}
|
||||
const older_versions = message.get('older_versions') || [];
|
||||
older_versions.push(message.get('message'));
|
||||
message.save({
|
||||
'message': _converse.chatboxes.getMessageBody(stanza),
|
||||
@ -355,39 +363,37 @@
|
||||
return stanza;
|
||||
},
|
||||
|
||||
sendMessageStanza (message) {
|
||||
const messageStanza = this.createMessageStanza(message);
|
||||
_converse.connection.send(messageStanza);
|
||||
sendMessageStanza (stanza) {
|
||||
_converse.connection.send(stanza);
|
||||
if (_converse.forward_messages) {
|
||||
// Forward the message, so that other connected resources are also aware of it.
|
||||
_converse.connection.send(
|
||||
$msg({
|
||||
'to': _converse.bare_jid,
|
||||
'type': this.get('message_type'),
|
||||
'id': message.get('msgid')
|
||||
}).c('forwarded', {'xmlns': Strophe.NS.FORWARD})
|
||||
.c('delay', {
|
||||
'xmns': Strophe.NS.DELAY,
|
||||
'stamp': moment().format()
|
||||
}).up()
|
||||
.cnode(messageStanza.tree())
|
||||
.cnode(stanza.tree())
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
getOutgoingMessageAttributes (text, spoiler_hint) {
|
||||
const fullname = _converse.xmppstatus.get('fullname'),
|
||||
is_spoiler = this.get('composing_spoiler');
|
||||
|
||||
return {
|
||||
'fullname': fullname,
|
||||
const is_spoiler = this.get('composing_spoiler');
|
||||
return _.extend(this.toJSON(), {
|
||||
'id': _converse.connection.getUniqueId(),
|
||||
'fullname': _converse.xmppstatus.get('fullname'),
|
||||
'from': _converse.bare_jid,
|
||||
'sender': 'me',
|
||||
'time': moment().format(),
|
||||
'message': text ? u.httpToGeoUri(emojione.shortnameToUnicode(text), _converse) : undefined,
|
||||
'is_spoiler': is_spoiler,
|
||||
'spoiler_hint': is_spoiler ? spoiler_hint : undefined
|
||||
};
|
||||
'spoiler_hint': is_spoiler ? spoiler_hint : undefined,
|
||||
'type': this.get('message_type')
|
||||
});
|
||||
},
|
||||
|
||||
sendMessage (attrs) {
|
||||
@ -396,7 +402,7 @@
|
||||
* Parameters:
|
||||
* (Message) message - The chat message
|
||||
*/
|
||||
const message = this.messages.findWhere('correcting')
|
||||
let message = this.messages.findWhere('correcting')
|
||||
if (message) {
|
||||
const older_versions = message.get('older_versions') || [];
|
||||
older_versions.push(message.get('message'));
|
||||
@ -407,9 +413,10 @@
|
||||
'older_versions': older_versions,
|
||||
'references': attrs.references
|
||||
});
|
||||
return this.sendMessageStanza(message);
|
||||
} else {
|
||||
message = this.messages.create(attrs);
|
||||
}
|
||||
return this.sendMessageStanza(this.messages.create(attrs));
|
||||
return this.sendMessageStanza(this.createMessageStanza(message));
|
||||
},
|
||||
|
||||
sendChatState () {
|
||||
@ -453,8 +460,7 @@
|
||||
this.getOutgoingMessageAttributes(), {
|
||||
'file': file,
|
||||
'progress': 0,
|
||||
'slot_request_url': slot_request_url,
|
||||
'type': this.get('message_type'),
|
||||
'slot_request_url': slot_request_url
|
||||
})
|
||||
);
|
||||
}
|
||||
@ -512,11 +518,7 @@
|
||||
if (attrs.type === 'groupchat') {
|
||||
attrs.from = stanza.getAttribute('from');
|
||||
attrs.nick = Strophe.unescapeNode(Strophe.getResourceFromJid(attrs.from));
|
||||
if (Strophe.getResourceFromJid(attrs.from) === this.get('nick')) {
|
||||
attrs.sender = 'me';
|
||||
} else {
|
||||
attrs.sender = 'them';
|
||||
}
|
||||
attrs.sender = attrs.nick === this.get('nick') ? 'me': 'them';
|
||||
} else {
|
||||
attrs.from = Strophe.getBareJidFromJid(stanza.getAttribute('from'));
|
||||
if (attrs.from === _converse.bare_jid) {
|
||||
@ -541,17 +543,27 @@
|
||||
/* Create a Backbone.Message object inside this chat box
|
||||
* based on the identified message stanza.
|
||||
*/
|
||||
const attrs = this.getMessageAttributesFromStanza(message, original_stanza);
|
||||
const is_csn = u.isOnlyChatStateNotification(attrs);
|
||||
if (is_csn && (attrs.is_delayed || (attrs.type === 'groupchat' && Strophe.getResourceFromJid(attrs.from) == this.get('nick')))) {
|
||||
// XXX: MUC leakage
|
||||
// No need showing delayed or our own CSN messages
|
||||
return;
|
||||
} else if (!is_csn && !attrs.file && !attrs.message && !attrs.oob_url && attrs.type !== 'error') {
|
||||
// TODO: handle <subject> messages (currently being done by ChatRoom)
|
||||
return;
|
||||
const that = this;
|
||||
function _create (attrs) {
|
||||
const is_csn = u.isOnlyChatStateNotification(attrs);
|
||||
if (is_csn && (attrs.is_delayed ||
|
||||
(attrs.type === 'groupchat' && Strophe.getResourceFromJid(attrs.from) == that.get('nick')))) {
|
||||
// XXX: MUC leakage
|
||||
// No need showing delayed or our own CSN messages
|
||||
return;
|
||||
} else if (!is_csn && !attrs.file && !attrs.message && !attrs.oob_url && attrs.type !== 'error') {
|
||||
// TODO: handle <subject> messages (currently being done by ChatRoom)
|
||||
return;
|
||||
} else {
|
||||
return that.messages.create(attrs);
|
||||
}
|
||||
}
|
||||
const result = this.getMessageAttributesFromStanza(message, original_stanza)
|
||||
if (typeof result.then === "function") {
|
||||
return new Promise((resolve, reject) => result.then(attrs => resolve(_create(attrs))));
|
||||
} else {
|
||||
return this.messages.create(attrs);
|
||||
const message = _create(result)
|
||||
return Promise.resolve(message);
|
||||
}
|
||||
},
|
||||
|
||||
@ -621,7 +633,7 @@
|
||||
|
||||
onConnected () {
|
||||
this.browserStorage = new Backbone.BrowserStorage.session(
|
||||
b64_sha1(`converse.chatboxes-${_converse.bare_jid}`));
|
||||
`converse.chatboxes-${_converse.bare_jid}`);
|
||||
this.registerMessageHandler();
|
||||
this.fetch({
|
||||
'add': true,
|
||||
@ -728,8 +740,11 @@
|
||||
if (chatbox && !chatbox.handleMessageCorrection(stanza)) {
|
||||
const msgid = stanza.getAttribute('id'),
|
||||
message = msgid && chatbox.messages.findWhere({msgid});
|
||||
if (!message) { // Only create the message when we're sure it's not a duplicate
|
||||
chatbox.incrementUnreadMsgCounter(chatbox.createMessage(stanza, original_stanza));
|
||||
if (!message) {
|
||||
// Only create the message when we're sure it's not a duplicate
|
||||
chatbox.createMessage(stanza, original_stanza)
|
||||
.then(msg => chatbox.incrementUnreadMsgCounter(msg))
|
||||
.catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
|
||||
}
|
||||
}
|
||||
_converse.emit('message', {'stanza': original_stanza, 'chatbox': chatbox});
|
||||
|
@ -110,9 +110,11 @@
|
||||
emojione.ascii = true;
|
||||
|
||||
function onWindowStateChanged (data) {
|
||||
_converse.chatboxviews.each(function (chatboxview) {
|
||||
chatboxview.onWindowStateChanged(data.state);
|
||||
});
|
||||
if (_converse.chatboxviews) {
|
||||
_converse.chatboxviews.each(chatboxview => {
|
||||
chatboxview.onWindowStateChanged(data.state);
|
||||
});
|
||||
}
|
||||
}
|
||||
_converse.api.listen.on('windowStateChanged', onWindowStateChanged);
|
||||
|
||||
@ -228,32 +230,30 @@
|
||||
|
||||
events: {
|
||||
'click button.remove-contact': 'removeContact',
|
||||
'click button.refresh-contact': 'refreshContact'
|
||||
'click button.refresh-contact': 'refreshContact',
|
||||
'click .fingerprint-trust .btn input': 'toggleDeviceTrust'
|
||||
},
|
||||
|
||||
initialize () {
|
||||
_converse.BootstrapModal.prototype.initialize.apply(this, arguments);
|
||||
this.model.on('contactAdded', this.registerContactEventHandlers, this);
|
||||
this.model.on('change', this.render, this);
|
||||
this.registerContactEventHandlers();
|
||||
_converse.emit('userDetailsModalInitialized', this.model);
|
||||
},
|
||||
|
||||
toHTML () {
|
||||
return tpl_user_details_modal(_.extend(
|
||||
this.model.toJSON(),
|
||||
this.model.vcard.toJSON(), {
|
||||
'_': _,
|
||||
'__': __,
|
||||
'view': this,
|
||||
'_converse': _converse,
|
||||
'allow_contact_removal': _converse.allow_contact_removal,
|
||||
'alt_profile_image': __("The User's Profile Image"),
|
||||
'display_name': this.model.getDisplayName(),
|
||||
'is_roster_contact': !_.isUndefined(this.model.contact),
|
||||
'label_close': __('Close'),
|
||||
'label_email': __('Email'),
|
||||
'label_fullname': __('Full Name'),
|
||||
'label_jid': __('Jabber ID'),
|
||||
'label_nickname': __('Nickname'),
|
||||
'label_remove': __('Remove as contact'),
|
||||
'label_refresh': __('Refresh'),
|
||||
'label_role': __('Role'),
|
||||
'label_url': __('URL')
|
||||
'utils': u
|
||||
}));
|
||||
},
|
||||
|
||||
@ -379,6 +379,7 @@
|
||||
this.addSpoilerButton(options);
|
||||
this.addFileUploadButton();
|
||||
this.insertEmojiPicker();
|
||||
_converse.emit('renderToolbar', this);
|
||||
return this;
|
||||
},
|
||||
|
||||
@ -737,14 +738,16 @@
|
||||
|
||||
if (!u.hasClass('chat-msg--action', el) && !u.hasClass('chat-msg--action', previous_el) &&
|
||||
previous_el.getAttribute('data-from') === from &&
|
||||
date.isBefore(moment(previous_el.getAttribute('data-isodate')).add(10, 'minutes'))) {
|
||||
date.isBefore(moment(previous_el.getAttribute('data-isodate')).add(10, 'minutes')) &&
|
||||
el.getAttribute('data-encrypted') === previous_el.getAttribute('data-encrypted')) {
|
||||
u.addClass('chat-msg--followup', el);
|
||||
}
|
||||
if (!next_el) { return; }
|
||||
|
||||
if (!u.hasClass('chat-msg--action', 'el') &&
|
||||
next_el.getAttribute('data-from') === from &&
|
||||
moment(next_el.getAttribute('data-isodate')).isBefore(date.add(10, 'minutes'))) {
|
||||
moment(next_el.getAttribute('data-isodate')).isBefore(date.add(10, 'minutes')) &&
|
||||
el.getAttribute('data-encrypted') === next_el.getAttribute('data-encrypted')) {
|
||||
u.addClass('chat-msg--followup', next_el);
|
||||
} else {
|
||||
u.removeClass('chat-msg--followup', next_el);
|
||||
|
@ -86,6 +86,7 @@
|
||||
'converse-muc',
|
||||
'converse-muc-views',
|
||||
'converse-notification',
|
||||
'converse-omemo',
|
||||
'converse-oauth',
|
||||
'converse-ping',
|
||||
'converse-profile',
|
||||
|
@ -6,7 +6,6 @@
|
||||
|
||||
/* This is a Converse.js plugin which add support for XEP-0030: Service Discovery */
|
||||
|
||||
/*global Backbone, define, window */
|
||||
(function (root, factory) {
|
||||
define(["converse-core", "sizzle"], factory);
|
||||
}(this, function (converse, sizzle) {
|
||||
|
@ -128,13 +128,21 @@
|
||||
//
|
||||
// New functions which don't exist yet can also be added.
|
||||
ChatBox: {
|
||||
|
||||
getMessageAttributesFromStanza (message, original_stanza) {
|
||||
const attrs = this.__super__.getMessageAttributesFromStanza.apply(this, arguments);
|
||||
const archive_id = getMessageArchiveID(original_stanza);
|
||||
if (archive_id) {
|
||||
attrs.archive_id = archive_id;
|
||||
function _process (attrs) {
|
||||
const archive_id = getMessageArchiveID(original_stanza);
|
||||
if (archive_id) {
|
||||
attrs.archive_id = archive_id;
|
||||
}
|
||||
return attrs;
|
||||
}
|
||||
const result = this.__super__.getMessageAttributesFromStanza.apply(this, arguments)
|
||||
if (result instanceof Promise) {
|
||||
return new Promise((resolve, reject) => result.then((attrs) => resolve(_process(attrs))).catch(reject));
|
||||
} else {
|
||||
return _process(result);
|
||||
}
|
||||
return attrs;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -158,7 +158,8 @@
|
||||
_.partial(u.renderImageURL, _converse))(url);
|
||||
}
|
||||
|
||||
let text = this.model.get('message');
|
||||
const encrypted = this.model.get('encrypted');
|
||||
let text = encrypted ? this.model.get('plaintext') : this.model.get('message');
|
||||
if (is_me_message) {
|
||||
text = text.replace(/^\/me/, '');
|
||||
}
|
||||
|
@ -428,11 +428,7 @@
|
||||
this.toggleview = new _converse.MinimizedChatsToggleView({
|
||||
'model': new _converse.MinimizedChatsToggle({'id': id})
|
||||
});
|
||||
try {
|
||||
this.toggleview.model.browserStorage = new Backbone.BrowserStorage[storage](id);
|
||||
} catch (e) {
|
||||
debugger;
|
||||
}
|
||||
this.toggleview.model.browserStorage = new Backbone.BrowserStorage[storage](id);
|
||||
this.toggleview.model.fetch();
|
||||
},
|
||||
|
||||
|
@ -65,6 +65,16 @@
|
||||
}
|
||||
});
|
||||
|
||||
_converse.api.listen.on('afterTearDown', () => {
|
||||
if (!_converse.chatboxviews) {
|
||||
return;
|
||||
}
|
||||
const container = _converse.chatboxviews.el.querySelector("#converse-modals");
|
||||
if (container) {
|
||||
container.innerHTML = '';
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/************************ BEGIN API ************************/
|
||||
// We extend the default converse.js API to add methods specific to MUC chat rooms.
|
||||
|
@ -926,7 +926,9 @@
|
||||
if (sender === '') {
|
||||
return;
|
||||
}
|
||||
this.incrementUnreadMsgCounter(this.createMessage(stanza, original_stanza));
|
||||
this.createMessage(stanza, original_stanza)
|
||||
.then(msg => this.incrementUnreadMsgCounter(msg))
|
||||
.catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
|
||||
}
|
||||
if (sender !== this.get('nick')) {
|
||||
// We only emit an event if it's not our own message
|
||||
|
1062
src/converse-omemo.js
Normal file
1062
src/converse-omemo.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -237,7 +237,7 @@
|
||||
this.trigger('showReceivedOTRMessage', msg);
|
||||
});
|
||||
this.otr.on('io', (msg) => {
|
||||
this.sendMessage(new _converse.Message({'message':msg}));
|
||||
this.sendMessage({'message':msg});
|
||||
});
|
||||
this.otr.on('error', (msg) => {
|
||||
this.trigger('showOTRError', msg);
|
||||
|
@ -48,32 +48,41 @@
|
||||
events: {
|
||||
'click .change-avatar': "openFileSelection",
|
||||
'change input[type="file"': "updateFilePreview",
|
||||
'submit form': 'onFormSubmitted'
|
||||
'submit .profile-form': 'onFormSubmitted'
|
||||
},
|
||||
|
||||
initialize () {
|
||||
_converse.BootstrapModal.prototype.initialize.apply(this, arguments);
|
||||
this.model.on('change', this.render, this);
|
||||
_converse.BootstrapModal.prototype.initialize.apply(this, arguments);
|
||||
_converse.emit('profileModalInitialized', this.model);
|
||||
},
|
||||
|
||||
toHTML () {
|
||||
return tpl_profile_modal(_.extend(
|
||||
this.model.toJSON(),
|
||||
this.model.vcard.toJSON(), {
|
||||
'_': _,
|
||||
'__': __,
|
||||
'_converse': _converse,
|
||||
'alt_avatar': __('Your avatar image'),
|
||||
'heading_profile': __('Your Profile'),
|
||||
'label_close': __('Close'),
|
||||
'label_email': __('Email'),
|
||||
'label_fullname': __('Full Name'),
|
||||
'label_nickname': __('Nickname'),
|
||||
'label_jid': __('XMPP Address (JID)'),
|
||||
'label_nickname': __('Nickname'),
|
||||
'label_role': __('Role'),
|
||||
'label_role_help': __('Use commas to separate multiple roles. Your roles are shown next to your name on your chat messages.'),
|
||||
'label_save': __('Save'),
|
||||
'label_url': __('URL'),
|
||||
'alt_avatar': __('Your avatar image')
|
||||
'utils': u,
|
||||
'view': this
|
||||
}));
|
||||
},
|
||||
|
||||
afterRender () {
|
||||
this.tabs = _.map(this.el.querySelectorAll('.nav-item'), (tab) => new bootstrap.Tab(tab));
|
||||
},
|
||||
|
||||
openFileSelection (ev) {
|
||||
ev.preventDefault();
|
||||
this.el.querySelector('input[type="file"]').click();
|
||||
|
@ -23,7 +23,7 @@ if (typeof define !== 'undefined') {
|
||||
"converse-muc-views",
|
||||
"converse-muc-views", // Views related to MUC
|
||||
"converse-notification", // HTML5 Notifications
|
||||
"converse-oauth",
|
||||
"converse-omemo",
|
||||
"converse-ping", // XEP-0199 XMPP Ping
|
||||
"converse-register", // XEP-0077 In-band registration
|
||||
"converse-roomslist", // Show currently open chat rooms
|
||||
|
@ -1,4 +1,5 @@
|
||||
<div class="message chat-msg {{{o.type}}} {[ if (o.is_me_message) { ]} chat-msg--action {[ } ]} {{{o.extra_classes}}}" data-isodate="{{{o.time}}}" data-msgid="{{{o.msgid}}}" data-from="{{{o.from}}}">
|
||||
<div class="message chat-msg {{{o.type}}} {[ if (o.is_me_message) { ]} chat-msg--action {[ } ]} {{{o.extra_classes}}}"
|
||||
data-isodate="{{{o.time}}}" data-msgid="{{{o.msgid}}}" data-from="{{{o.from}}}" data-encrypted="{{{o.is_encrypted}}}">
|
||||
{[ if (o.type !== 'headline' && !o.is_me_message) { ]}
|
||||
<canvas class="avatar chat-msg__avatar" height="36" width="36"></canvas>
|
||||
{[ } ]}
|
||||
@ -9,6 +10,7 @@
|
||||
{[o.roles.forEach(function (role) { ]} <span class="badge badge-secondary">{{{role}}}</span> {[ }); ]}
|
||||
</span>
|
||||
{[ if (!o.is_me_message) { ]}<time timestamp="{{{o.isodate}}}" class="chat-msg__time">{{{o.pretty_time}}}</time>{[ } ]}
|
||||
{[ if (o.is_encrypted) { ]}<span class="fa fa-lock"></span>{[ } ]}
|
||||
</span>
|
||||
{[ if (!o.is_me_message) { ]}<div class="chat-msg__body">{[ } ]}
|
||||
{[ if (o.edited) { ]} <i title="{{{o.__('This message has been edited')}}}" class="fa fa-edit chat-msg__edit-modal"></i> {[ } ]}
|
||||
|
@ -5,54 +5,117 @@
|
||||
<h5 class="modal-title" id="user-profile-modal-label">{{{o.heading_profile}}}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="{{{o.label_close}}}"><span aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<form class="converse-form">
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-auto">
|
||||
<a class="change-avatar" href="#">
|
||||
{[ if (o.image) { ]}
|
||||
<img alt="{{{o.alt_avatar}}}" class="img-thumbnail avatar align-self-center" height="100px" width="100px" src="data:{{{o.image_type}}};base64,{{{o.image}}}"/>
|
||||
{[ } ]}
|
||||
{[ if (!o.image) { ]}
|
||||
<canvas class="avatar" height="100px" width="100px"/>
|
||||
{[ } ]}
|
||||
</a>
|
||||
<input class="hidden" name="image" type="file">
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="form-group">
|
||||
<label class="col-form-label">{{{o.label_jid}}}:</label>
|
||||
<div>{{{o.jid}}}</div>
|
||||
<div class="modal-body">
|
||||
{[ if (o._converse.pluggable.plugins['converse-omemo'].enabled(o._converse)) { ]}
|
||||
<ul class="nav nav-pills justify-content-center">
|
||||
<li role="presentation" class="nav-item">
|
||||
<a class="nav-link active" id="profile-tab" href="#profile-tabpanel" aria-controls="profile-tabpanel" role="tab" data-toggle="tab">Profile</a>
|
||||
</li>
|
||||
<li role="presentation" class="nav-item">
|
||||
<a class="nav-link" id="omemo-tab" href="#omemo-tabpanel" aria-controls="omemo-tabpanel" role="tab" data-toggle="tab">OMEMO</a>
|
||||
</li>
|
||||
</ul>
|
||||
{[ } ]}
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade show active" id="profile-tabpanel" role="tabpanel" aria-labelledby="profile-tab">
|
||||
<form class="converse-form converse-form--modal profile-form" action="#">
|
||||
<div class="row">
|
||||
<div class="col-auto">
|
||||
<a class="change-avatar" href="#">
|
||||
{[ if (o.image) { ]}
|
||||
<img alt="{{{o.alt_avatar}}}" class="img-thumbnail avatar align-self-center" height="100px" width="100px" src="data:{{{o.image_type}}};base64,{{{o.image}}}"/>
|
||||
{[ } ]}
|
||||
{[ if (!o.image) { ]}
|
||||
<canvas class="avatar" height="100px" width="100px"/>
|
||||
{[ } ]}
|
||||
</a>
|
||||
<input class="hidden" name="image" type="file">
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="form-group">
|
||||
<label class="col-form-label">{{{o.label_jid}}}:</label>
|
||||
<div>{{{o.jid}}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="vcard-fullname" class="col-form-label">{{{o.label_fullname}}}:</label>
|
||||
<input id="vcard-fullname" type="text" class="form-control" name="fn" value="{{{o.fullname}}}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="vcard-nickname" class="col-form-label">{{{o.label_nickname}}}:</label>
|
||||
<input id="vcard-nickname" type="text" class="form-control" name="nickname" value="{{{o.nickname}}}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="vcard-url" class="col-form-label">{{{o.label_url}}}:</label>
|
||||
<input id="vcard-url" type="url" class="form-control" name="url" value="{{{o.url}}}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="vcard-email" class="col-form-label">{{{o.label_email}}}:</label>
|
||||
<input id="vcard-email" type="email" class="form-control" name="email" value="{{{o.email}}}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="vcard-role" class="col-form-label">{{{o.label_role}}}:</label>
|
||||
<input id="vcard-role" type="text" class="form-control" name="role" value="{{{o.role}}}" aria-describedby="vcard-role-help">
|
||||
<small id="vcard-role-help" class="form-text text-muted">{{{o.label_role_help}}}</small>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="save-form btn btn-primary">{{{o.__('Save and close')}}}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{[ if (o._converse.pluggable.plugins['converse-omemo'].enabled(o._converse)) { ]}
|
||||
<div class="tab-pane fade" id="omemo-tabpanel" role="tabpanel" aria-labelledby="omemo-tab">
|
||||
<form class="converse-form fingerprint-removal">
|
||||
<ul class="list-group fingerprints">
|
||||
<li class="list-group-item active">{{{o.__("This device's OMEMO fingerprint")}}}</li>
|
||||
<li class="list-group-item">
|
||||
{[ if (o.view.current_device.get('bundle') && o.view.current_device.get('bundle').fingerprint) { ]}
|
||||
<span class="fingerprint">{{{o.utils.formatFingerprint(o.view.current_device.get('bundle').fingerprint)}}}</span>
|
||||
{[ } else {]}
|
||||
<span class="spinner fa fa-spinner centered"/>
|
||||
{[ } ]}
|
||||
</li>
|
||||
</ul>
|
||||
{[ if (o.view.other_devices.length) { ]}
|
||||
<ul class="list-group fingerprints">
|
||||
<li class="list-group-item nopadding active">
|
||||
<label>
|
||||
<input type="checkbox" class="select-all" title="{{{o.__('Select all')}}}"
|
||||
aria-label="{{{o.__('Checkbox to select fingerprints of all other OMEMO devices')}}}">
|
||||
{{{o.__('Other OMEMO-enabled devices')}}}
|
||||
</label>
|
||||
</li>
|
||||
{[ o._.forEach(o.view.other_devices, function (device) { ]}
|
||||
{[ if (device.get('bundle') && device.get('bundle').fingerprint) { ]}
|
||||
<li class="fingerprint-removal-item list-group-item nopadding">
|
||||
<label>
|
||||
<input type="checkbox" value="{{{device.get('id')}}}"
|
||||
aria-label="{{{o.__('Checkbox for selecting the following fingerprint')}}}">
|
||||
<span class="fingerprint">{{{o.utils.formatFingerprint(device.get('bundle').fingerprint)}}}</span>
|
||||
</label>
|
||||
</li>
|
||||
{[ } else {]}
|
||||
<li class="fingerprint-removal-item list-group-item nopadding">
|
||||
<label>
|
||||
<input type="checkbox" value="{{{device.get('id')}}}"
|
||||
aria-label="{{{o.__('Checkbox for selecting the following fingerprint')}}}">
|
||||
<span>{{{o.__('Device without a fingerprint')}}}</span>
|
||||
</label>
|
||||
</li>
|
||||
{[ } ]}
|
||||
{[ }); ]}
|
||||
</ul>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="save-form btn btn-primary">{{{o.__('Remove checked devices and close')}}}</button>
|
||||
</div>
|
||||
{[ } ]}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="vcard-fullname" class="col-form-label">{{{o.label_fullname}}}:</label>
|
||||
<input id="vcard-fullname" type="text" class="form-control" name="fn" value="{{{o.fullname}}}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="vcard-nickname" class="col-form-label">{{{o.label_nickname}}}:</label>
|
||||
<input id="vcard-nickname" type="text" class="form-control" name="nickname" value="{{{o.nickname}}}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="vcard-url" class="col-form-label">{{{o.label_url}}}:</label>
|
||||
<input id="vcard-url" type="url" class="form-control" name="url" value="{{{o.url}}}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="vcard-email" class="col-form-label">{{{o.label_email}}}:</label>
|
||||
<input id="vcard-email" type="email" class="form-control" name="email" value="{{{o.email}}}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="vcard-role" class="col-form-label">{{{o.label_role}}}:</label>
|
||||
<input id="vcard-role" type="text" class="form-control" name="role" value="{{{o.role}}}" aria-describedby="vcard-role-help">
|
||||
<small id="vcard-role-help" class="form-text text-muted">{{{o.label_role_help}}}</small>
|
||||
</div>
|
||||
{[ } ]}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="save-form btn btn-primary">{{{o.label_save}}}</button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{{o.label_close}}}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
1
src/templates/toolbar_omemo.html
Normal file
1
src/templates/toolbar_omemo.html
Normal file
@ -0,0 +1 @@
|
||||
<li class="toggle-omemo fa {[ if (o.omemo_active) { ]} fa-lock {[ } else { ]} fa-unlock {[ } ]}" title="{{{o.__('Messages are being sent in plaintext')}}}"></li>
|
@ -1,39 +1,70 @@
|
||||
<div class="modal fade" id="user-profile-modal" tabindex="-1" role="dialog" aria-labelledby="user-profile-modal-label" aria-hidden="true">
|
||||
<div class="modal fade" id="user-details-modal" tabindex="-1" role="dialog" aria-labelledby="user-details-modal-label" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="user-profile-modal-label">{{{o.display_name}}}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="{{{o.label_close}}}"><span aria-hidden="true">×</span></button>
|
||||
<h5 class="modal-title" id="user-details-modal-label">{{{o.display_name}}}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="{{{o.__('Close')}}}"><span aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{[ if (o.image) { ]}
|
||||
<img alt="{{{o.alt_profile_image}}}"
|
||||
<img alt="{{{o.__('The User\'s Profile Image')}}}"
|
||||
class="img-thumbnail avatar align-self-center mb-3"
|
||||
height="100" width="100" src="data:{{{o.image_type}}};base64,{{{o.image}}}"/>
|
||||
{[ } ]}
|
||||
{[ if (o.fullname) { ]}
|
||||
<p><label>{{{o.label_fullname}}}:</label> {{{o.fullname}}}</p>
|
||||
<p><label>{{{o.__('Full Name')}}}:</label> {{{o.fullname}}}</p>
|
||||
{[ } ]}
|
||||
<p><label>{{{o.label_jid}}}:</label> {{{o.jid}}}</p>
|
||||
<p><label>{{{o.__('XMPP Address')}}}:</label> {{{o.jid}}}</p>
|
||||
{[ if (o.nickname) { ]}
|
||||
<p><label>{{{o.label_nickname}}}:</label> {{{o.nickname}}}</p>
|
||||
<p><label>{{{o.__('Nickname')}}}:</label> {{{o.nickname}}}</p>
|
||||
{[ } ]}
|
||||
{[ if (o.url) { ]}
|
||||
<p><label>{{{o.label_url}}}:</label> <a target="_blank" rel="noopener" href="{{{o.url}}}">{{{o.url}}}</a></p>
|
||||
<p><label>{{{o.__('URL')}}}:</label> <a target="_blank" rel="noopener" href="{{{o.url}}}">{{{o.url}}}</a></p>
|
||||
{[ } ]}
|
||||
{[ if (o.email) { ]}
|
||||
<p><label>{{{o.label_email}}}:</label> <a href="mailto:{{{o.email}}}">{{{o.email}}}</a></p>
|
||||
<p><label>{{{o.__('Email')}}}:</label> <a href="mailto:{{{o.email}}}">{{{o.email}}}</a></p>
|
||||
{[ } ]}
|
||||
{[ if (o.role) { ]}
|
||||
<p><label>{{{o.label_role}}}:</label> {{{o.role}}}</p>
|
||||
<p><label>{{{o.__('Role')}}}:</label> {{{o.role}}}</p>
|
||||
{[ } ]}
|
||||
|
||||
{[ if (o._converse.pluggable.plugins['converse-omemo'].enabled(o._converse)) { ]}
|
||||
<hr>
|
||||
<ul class="list-group fingerprints">
|
||||
<li class="list-group-item active">{{{o.__('OMEMO Fingerprints')}}}</li>
|
||||
{[ if (!o.view.devicelist.devices) { ]}
|
||||
<li class="list-group-item"><span class="spinner fa fa-spinner centered"/></li>
|
||||
{[ } ]}
|
||||
{[ if (o.view.devicelist.devices) { ]}
|
||||
{[ o.view.devicelist.devices.each(function (device) { ]}
|
||||
{[ if (device.get('bundle') && device.get('bundle').fingerprint) { ]}
|
||||
<li class="list-group-item">
|
||||
<form class="fingerprint-trust">
|
||||
<div class="btn-group btn-group-toggle">
|
||||
<label class="btn btn--small {[ if (device.get('trusted') !== -1) { ]} btn-primary active {[ } else { ]} btn-secondary {[ } ]}">
|
||||
<input type="radio" name="{{{device.get('id')}}}" value="1"
|
||||
{[ if (device.get('trusted') !== -1) { ]} checked="checked" {[ } ]}>{{{o.__('Trusted')}}}
|
||||
</label>
|
||||
<label class="btn btn--small {[ if (device.get('trusted') === -1) { ]} btn-primary active {[ } else { ]} btn-secondary {[ } ]}">
|
||||
<input type="radio" name="{{{device.get('id')}}}" value="-1"
|
||||
{[ if (device.get('trusted') === -1) { ]} checked="checked" {[ } ]}>{{{o.__('Untrusted')}}}
|
||||
</label>
|
||||
</div>
|
||||
<span class="fingerprint">{{{o.utils.formatFingerprint(device.get('bundle').fingerprint)}}}</span>
|
||||
</form>
|
||||
</li>
|
||||
{[ } ]}
|
||||
{[ }); ]}
|
||||
{[ } ]}
|
||||
</ul>
|
||||
{[ } ]}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
{[ if (o.allow_contact_removal && o.is_roster_contact) { ]}
|
||||
<button type="button" class="btn btn-danger remove-contact"><i class="fa fa-trash"> </i>{{{o.label_remove}}}</button>
|
||||
<button type="button" class="btn btn-danger remove-contact"><i class="fa fa-trash"> </i>{{{o.__('Remove as contact')}}}</button>
|
||||
{[ } ]}
|
||||
<button type="button" class="btn btn-info refresh-contact"><i class="fa fa-refresh"> </i>{{{o.label_refresh}}}</button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{{o.label_close}}}</button>
|
||||
<button type="button" class="btn btn-info refresh-contact"><i class="fa fa-refresh"> </i>{{{o.__('Refresh')}}}</button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{{o.__('Close')}}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -6,7 +6,7 @@
|
||||
// Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
|
||||
// Licensed under the Mozilla Public License (MPLv2)
|
||||
//
|
||||
/*global define, escape, window */
|
||||
/*global define, escape, window, Uint8Array */
|
||||
(function (root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define([
|
||||
@ -900,14 +900,11 @@
|
||||
return text.replace(_converse.geouri_regex, replacement);
|
||||
};
|
||||
|
||||
u.getSelectValues = function(select) {
|
||||
var result = [];
|
||||
var options = select && select.options;
|
||||
var opt;
|
||||
|
||||
u.getSelectValues = function (select) {
|
||||
const result = [];
|
||||
const options = select && select.options;
|
||||
for (var i=0, iLen=options.length; i<iLen; i++) {
|
||||
opt = options[i];
|
||||
|
||||
const opt = options[i];
|
||||
if (opt.selected) {
|
||||
result.push(opt.value || opt.text);
|
||||
}
|
||||
@ -915,6 +912,50 @@
|
||||
return result;
|
||||
};
|
||||
|
||||
u.formatFingerprint = function (fp) {
|
||||
fp = fp.replace(/^05/, '');
|
||||
const arr = [];
|
||||
for (let i=1; i<8; i++) {
|
||||
const idx = i*8+i-1;
|
||||
fp = fp.slice(0, idx) + ' ' + fp.slice(idx);
|
||||
}
|
||||
return fp;
|
||||
};
|
||||
|
||||
u.arrayBufferToHex = function (ab) {
|
||||
// https://stackoverflow.com/questions/40031688/javascript-arraybuffer-to-hex#40031979
|
||||
return Array.prototype.map.call(new Uint8Array(ab), x => ('00' + x.toString(16)).slice(-2)).join('');
|
||||
};
|
||||
|
||||
u.arrayBufferToString = function (ab) {
|
||||
const enc = new TextDecoder("utf-8");
|
||||
return enc.decode(ab);
|
||||
};
|
||||
|
||||
u.arrayBufferToBase64 = function (ab) {
|
||||
return btoa((new Uint8Array(ab)).reduce((data, byte) => data + String.fromCharCode(byte), ''));
|
||||
};
|
||||
|
||||
u.stringToArrayBuffer = function (string) {
|
||||
const enc = new TextEncoder(); // always utf-8
|
||||
return enc.encode(string);
|
||||
};
|
||||
|
||||
u.base64ToArrayBuffer = function (b64) {
|
||||
const binary_string = window.atob(b64),
|
||||
len = binary_string.length,
|
||||
bytes = new Uint8Array(len);
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
bytes[i] = binary_string.charCodeAt(i)
|
||||
}
|
||||
return bytes.buffer
|
||||
};
|
||||
|
||||
u.getRandomInt = function (max) {
|
||||
return Math.floor(Math.random() * Math.floor(max));
|
||||
};
|
||||
|
||||
u.putCurserAtEnd = function (textarea) {
|
||||
if (textarea !== document.activeElement) {
|
||||
textarea.focus();
|
||||
|
@ -3,7 +3,7 @@
|
||||
//
|
||||
// This is the utilities module.
|
||||
//
|
||||
// Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
|
||||
// Copyright (c) 2013-2018, Jan-Carel Brand <jc@opkode.com>
|
||||
// Licensed under the Mozilla Public License (MPLv2)
|
||||
//
|
||||
/*global define, escape, Jed */
|
||||
|
@ -6,8 +6,68 @@
|
||||
var Strophe = converse.env.Strophe;
|
||||
var moment = converse.env.moment;
|
||||
var $iq = converse.env.$iq;
|
||||
var mock = {};
|
||||
var u = converse.env.utils;
|
||||
|
||||
window.libsignal = {
|
||||
'SignalProtocolAddress': function (name, device_id) {
|
||||
this.name = name;
|
||||
this.deviceId = device_id;
|
||||
},
|
||||
'SessionCipher': function (storage, remote_address) {
|
||||
this.remoteAddress = remote_address;
|
||||
this.storage = storage;
|
||||
this.encrypt = () => Promise.resolve({
|
||||
'type': 1,
|
||||
'body': 'c1ph3R73X7',
|
||||
'registrationId': '1337'
|
||||
});
|
||||
this.decryptPreKeyWhisperMessage = (key_and_tag) => {
|
||||
// TODO: remove the prekey
|
||||
return Promise.resolve(u.stringToArrayBuffer(key_and_tag));
|
||||
};
|
||||
|
||||
this.decryptWhisperMessage = (key_and_tag) => {
|
||||
return Promise.resolve(u.stringToArrayBuffer(key_and_tag));
|
||||
}
|
||||
},
|
||||
'SessionBuilder': function (storage, remote_address) {
|
||||
this.processPreKey = function () {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
'KeyHelper': {
|
||||
'generateIdentityKeyPair': function () {
|
||||
return Promise.resolve({
|
||||
'pubKey': new TextEncoder('utf-8').encode('1234'),
|
||||
'privKey': new TextEncoder('utf-8').encode('4321')
|
||||
});
|
||||
},
|
||||
'generateRegistrationId': function () {
|
||||
return '123456789';
|
||||
},
|
||||
'generatePreKey': function (keyid) {
|
||||
return Promise.resolve({
|
||||
'keyId': keyid,
|
||||
'keyPair': {
|
||||
'pubKey': new TextEncoder('utf-8').encode('1234'),
|
||||
'privKey': new TextEncoder('utf-8').encode('4321')
|
||||
}
|
||||
});
|
||||
},
|
||||
'generateSignedPreKey': function (identity_keypair, keyid) {
|
||||
return Promise.resolve({
|
||||
'signature': new TextEncoder('utf-8').encode('11112222333344445555'),
|
||||
'keyId': keyid,
|
||||
'keyPair': {
|
||||
'pubKey': new TextEncoder('utf-8').encode('1234'),
|
||||
'privKey': new TextEncoder('utf-8').encode('4321')
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var mock = {};
|
||||
mock.view_mode = 'overlayed';
|
||||
|
||||
// Names from http://www.fakenamegenerator.com/
|
||||
|
@ -1,102 +0,0 @@
|
||||
/*global config */
|
||||
|
||||
// Extra test dependencies
|
||||
config.baseUrl = '../';
|
||||
config.paths.jquery = "node_modules/jquery/dist/jquery";
|
||||
config.paths.mock = "tests/mock";
|
||||
config.paths['wait-until-promise'] = "node_modules/wait-until-promise/index";
|
||||
config.paths['test-utils'] = "tests/utils";
|
||||
config.paths.sinon = "node_modules/sinon/pkg/sinon";
|
||||
config.paths.transcripts = "converse-logs/converse-logs";
|
||||
config.paths["jasmine-core"] = "node_modules/jasmine-core/lib/jasmine-core/jasmine";
|
||||
config.paths.jasmine = "node_modules/jasmine-core/lib/jasmine-core/boot";
|
||||
config.paths["jasmine-console"] = "node_modules/jasmine-core/lib/console/console";
|
||||
config.paths["console-reporter"] = "tests/console-reporter";
|
||||
config.paths["jasmine-html"] = "node_modules/jasmine-core/lib/jasmine-core/jasmine-html";
|
||||
|
||||
config.paths.converse = "builds/converse";
|
||||
config.paths.utils = "builds/utils";
|
||||
config.paths["converse-bookmarks"] = "builds/converse-bookmarks";
|
||||
config.paths["converse-chatboxes"] = "builds/converse-chatboxes";
|
||||
config.paths["converse-chatview"] = "builds/converse-chatview";
|
||||
config.paths["converse-controlbox"] = "builds/converse-controlbox";
|
||||
config.paths["converse-core"] = "builds/converse-core";
|
||||
config.paths["converse-disco"] = "builds/converse-disco";
|
||||
config.paths["converse-dragresize"] = "builds/converse-dragresize";
|
||||
config.paths["converse-headline"] = "builds/converse-headline";
|
||||
config.paths["converse-http-file-upload"]="builds/converse-http-file-upload";
|
||||
config.paths["converse-fullscreen"] = "builds/converse-fullscreen";
|
||||
config.paths["converse-mam"] = "builds/converse-mam";
|
||||
config.paths["converse-minimize"] = "builds/converse-minimize";
|
||||
config.paths["converse-muc"] = "builds/converse-muc";
|
||||
config.paths["converse-muc-embedded"] = "builds/converse-muc-embedded";
|
||||
config.paths["converse-notification"] = "builds/converse-notification";
|
||||
config.paths["converse-otr"] = "builds/converse-otr";
|
||||
config.paths["converse-ping"] = "builds/converse-ping";
|
||||
config.paths["converse-profile"] = "builds/converse-profile";
|
||||
config.paths["converse-register"] = "builds/converse-register";
|
||||
config.paths["converse-roomslist"] = "builds/converse-roomslist";
|
||||
config.paths["converse-rosterview"] = "builds/converse-rosterview";
|
||||
config.paths["converse-singleton"] = "builds/converse-singleton";
|
||||
config.paths["converse-vcard"] = "builds/converse-vcard";
|
||||
|
||||
|
||||
config.shim.jasmine = {
|
||||
exports: 'window.jasmineRequire'
|
||||
};
|
||||
config.shim['jasmine-html'] = {
|
||||
deps: ['jasmine-core'],
|
||||
exports: 'window.jasmineRequire'
|
||||
};
|
||||
config.shim['jasmine-console'] = {
|
||||
deps: ['jasmine-core'],
|
||||
exports: 'window.jasmineRequire'
|
||||
};
|
||||
config.shim.jasmine = {
|
||||
deps: ['jasmine-core', 'jasmine-html', 'jasmine-console'],
|
||||
exports: 'window.jasmine'
|
||||
};
|
||||
require.config(config);
|
||||
|
||||
var specs = [
|
||||
"jasmine",
|
||||
//"spec/transcripts",
|
||||
"spec/profiling",
|
||||
"spec/utils",
|
||||
"spec/converse",
|
||||
"spec/bookmarks",
|
||||
"spec/roomslist",
|
||||
"spec/headline",
|
||||
"spec/disco",
|
||||
"spec/protocol",
|
||||
"spec/presence",
|
||||
"spec/eventemitter",
|
||||
"spec/ping",
|
||||
"spec/xmppstatus",
|
||||
"spec/mam",
|
||||
"spec/otr",
|
||||
"spec/controlbox",
|
||||
"spec/roster",
|
||||
"spec/chatbox",
|
||||
"spec/chatroom",
|
||||
"spec/minchats",
|
||||
"spec/notification",
|
||||
"spec/register"
|
||||
];
|
||||
|
||||
require(['console-reporter', 'mock', 'sinon', 'wait-until-promise', 'pluggable'],
|
||||
function(ConsoleReporter, mock, sinon, waitUntilPromise, pluggable) {
|
||||
window.sinon = sinon;
|
||||
waitUntilPromise.setPromiseImplementation(window.Promise);
|
||||
window.waitUntilPromise = waitUntilPromise.default;
|
||||
|
||||
window.localStorage.clear();
|
||||
window.sessionStorage.clear();
|
||||
|
||||
// Load the specs
|
||||
require(specs, function (jasmine) {
|
||||
var jasmineEnv = jasmine.getEnv();
|
||||
jasmineEnv.addReporter(new ConsoleReporter());
|
||||
window.onload();
|
||||
});
|
||||
});
|
@ -179,6 +179,7 @@ require.config(config);
|
||||
var specs = [
|
||||
"jasmine",
|
||||
//"spec/transcripts",
|
||||
//"spec/otr",
|
||||
"spec/spoilers",
|
||||
"spec/profiling",
|
||||
"spec/utils",
|
||||
@ -194,7 +195,7 @@ var specs = [
|
||||
"spec/push",
|
||||
"spec/xmppstatus",
|
||||
"spec/mam",
|
||||
// "spec/otr",
|
||||
"spec/omemo",
|
||||
"spec/controlbox",
|
||||
"spec/roster",
|
||||
"spec/chatbox",
|
||||
|
@ -100,7 +100,7 @@
|
||||
|
||||
utils.openChatBoxFor = function (_converse, jid) {
|
||||
_converse.roster.get(jid).trigger("open");
|
||||
return utils.waitUntil(() => _converse.chatboxviews.get(jid));
|
||||
return utils.waitUntil(() => _converse.chatboxviews.get(jid), 1000);
|
||||
};
|
||||
|
||||
utils.openChatRoomViaModal = function (_converse, jid, nick='') {
|
||||
|
Loading…
Reference in New Issue
Block a user